Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the iframe provider #552

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/providers/iframe-provider.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Networkish } from '@ethersproject/networks';
import { Web3Provider } from './web3-provider';
interface IFrameProviderOptions {
timeoutMs?: number;
targetOrigin?: string;
}
export default class IFrameProvider extends Web3Provider {
constructor(options?: IFrameProviderOptions, network?: Networkish);
}
export {};
87 changes: 87 additions & 0 deletions packages/providers/iframe-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var web3_provider_1 = require("./web3-provider");
var IFrameEthereumProvider = /** @class */ (function () {
function IFrameEthereumProvider(_a) {
var _b = _a === void 0 ? {} : _a, _c = _b.timeoutMs, timeoutMs = _c === void 0 ? 60000 : _c, _d = _b.targetOrigin, targetOrigin = _d === void 0 ? '*' : _d;
this.listenerAttached = false;
this.callbackMap = {};
this.targetOrigin = targetOrigin;
this.timeoutMs = timeoutMs;
}
/**
* Return true if the current context is a window within an iframe.
*/
IFrameEthereumProvider.isWithinIframe = function () {
return window && window.parent && window.parent !== window.self;
};
/**
* Handles events from the parent window
* @param event received from the parent window
*/
IFrameEthereumProvider.prototype.handleEvents = function (event) {
if (event.origin === this.targetOrigin) {
if (event.data && this.callbackMap[event.data.id]) {
this.callbackMap[event.data.id](null, event.data);
// Remove the resolver from the map so it is not rejected.
delete this.callbackMap[event.data.id];
}
}
};
/**
* Attach the message listener only once for this provider.
*/
IFrameEthereumProvider.prototype.attachListenerOnce = function () {
if (this.listenerAttached) {
return;
}
this.listenerAttached = true;
window.addEventListener('message', this.handleEvents);
};
/**
* Send a JSON RPC to the parent window.
*/
IFrameEthereumProvider.prototype.sendAsync = function (request, callback) {
var _this = this;
if (!IFrameEthereumProvider.isWithinIframe()) {
throw new Error('Not embedded in an iframe.');
}
var parentWindow = window && window.parent;
this.attachListenerOnce();
var id = request.id;
this.callbackMap[id] = callback;
parentWindow.postMessage(request, this.targetOrigin);
setTimeout(function () {
if (_this.callbackMap[id]) {
callback(new Error("The RPC to the parent iframe has timed out after " + _this.timeoutMs + "ms"), null);
}
// We no longer care about the result of the RPC after the time out.
delete _this.callbackMap[id];
}, this.timeoutMs);
};
return IFrameEthereumProvider;
}());
var IFrameProvider = /** @class */ (function (_super) {
__extends(IFrameProvider, _super);
function IFrameProvider(options, network) {
var _this = this;
var executor = new IFrameEthereumProvider(options);
_this = _super.call(this, executor, network) || this;
return _this;
}
return IFrameProvider;
}(web3_provider_1.Web3Provider));
exports.default = IFrameProvider;
3 changes: 2 additions & 1 deletion packages/providers/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ import { JsonRpcProvider, JsonRpcSigner } from "./json-rpc-provider";
import { NodesmithProvider } from "./nodesmith-provider";
import { Web3Provider } from "./web3-provider";
import { AsyncSendable } from "./web3-provider";
export { Provider, BaseProvider, FallbackProvider, AlchemyProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, NodesmithProvider, Web3Provider, IpcProvider, JsonRpcSigner, getNetwork, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable, Network, Networkish };
import IFrameProvider from './iframe-provider';
export { Provider, BaseProvider, FallbackProvider, AlchemyProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, NodesmithProvider, Web3Provider, IFrameProvider, IpcProvider, JsonRpcSigner, getNetwork, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable, Network, Networkish };
5 changes: 5 additions & 0 deletions packages/providers/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var abstract_provider_1 = require("@ethersproject/abstract-provider");
exports.Provider = abstract_provider_1.Provider;
Expand All @@ -23,3 +26,5 @@ var nodesmith_provider_1 = require("./nodesmith-provider");
exports.NodesmithProvider = nodesmith_provider_1.NodesmithProvider;
var web3_provider_1 = require("./web3-provider");
exports.Web3Provider = web3_provider_1.Web3Provider;
var iframe_provider_1 = __importDefault(require("./iframe-provider"));
exports.IFrameProvider = iframe_provider_1.default;
94 changes: 94 additions & 0 deletions packages/providers/src.ts/iframe-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Networkish } from '@ethersproject/networks';
import { AsyncSendable, JsonRpc, Web3Provider } from './web3-provider';

interface IFrameProviderOptions {
// How long in milliseconds to wait for a response from the parent window before timing out.
timeoutMs?: number;

// The origin that should be allowed to host this DAPP. Passed directly to Window#postMessage.
targetOrigin?: string;
}

class IFrameEthereumProvider implements AsyncSendable {
public readonly isMetaMask: false;

private readonly targetOrigin: string;
private readonly timeoutMs: number;
private listenerAttached: boolean = false;
private callbackMap: { [ rpcId: number ]: (error: any, data: any) => void } = {};

constructor({ timeoutMs = 60000, targetOrigin = '*' }: IFrameProviderOptions = {}) {
this.targetOrigin = targetOrigin;
this.timeoutMs = timeoutMs;
}

/**
* Return true if the current context is a window within an iframe.
*/
public static isWithinIframe(): boolean {
return window && window.parent && window.parent !== window.self;
}

/**
* Handles events from the parent window
* @param event received from the parent window
*/
private handleEvents(event: MessageEvent): void {
if (event.origin === this.targetOrigin) {
if (event.data && this.callbackMap[ event.data.id ]) {
this.callbackMap[ event.data.id ](null, event.data);

// Remove the resolver from the map so it is not rejected.
delete this.callbackMap[ event.data.id ];
}
}
}

/**
* Attach the message listener only once for this provider.
*/
private attachListenerOnce(): void {
if (this.listenerAttached) {
return;
}

this.listenerAttached = true;

window.addEventListener('message', this.handleEvents);
}

/**
* Send a JSON RPC to the parent window.
*/
public sendAsync(request: JsonRpc, callback: (error: any, response: any) => void): void {
if (!IFrameEthereumProvider.isWithinIframe()) {
throw new Error('Not embedded in an iframe.');
}

const parentWindow = window && window.parent;

this.attachListenerOnce();

const id = request.id;

this.callbackMap[ id ] = callback;

parentWindow.postMessage(request, this.targetOrigin);

setTimeout(() => {
if (this.callbackMap[ id ]) {
callback(new Error(`The RPC to the parent iframe has timed out after ${this.timeoutMs}ms`), null);
}

// We no longer care about the result of the RPC after the time out.
delete this.callbackMap[ id ];
}, this.timeoutMs);
}
}

export default class IFrameProvider extends Web3Provider {
constructor(options?: IFrameProviderOptions, network?: Networkish) {
const executor = new IFrameEthereumProvider(options);
super(executor, network);
}
}
3 changes: 2 additions & 1 deletion packages/providers/src.ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { NodesmithProvider } from "./nodesmith-provider";
import { Web3Provider } from "./web3-provider";

import { AsyncSendable } from "./web3-provider";
import IFrameProvider from './iframe-provider';


////////////////////////
Expand All @@ -51,7 +52,7 @@ export {
JsonRpcProvider,
NodesmithProvider,
Web3Provider,

IFrameProvider,
IpcProvider,


Expand Down
23 changes: 20 additions & 3 deletions packages/providers/src.ts/web3-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import { defineReadOnly } from "@ethersproject/properties";
import { JsonRpcProvider } from "./json-rpc-provider";


export type JsonRpc<TMethod = any, TParams = any> = {
jsonrpc: '2.0';
id: number;
method: TMethod;
params: TParams;
}

// Exported Types
export type AsyncSendable = {
isMetaMask?: boolean;
host?: string;
path?: string;
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void
send?: (request: any, callback: (error: any, response: any) => void) => void
sendAsync?: (request: JsonRpc, callback: (error: any, response: any) => void) => void
send?: (request: JsonRpc, callback: (error: any, response: any) => void) => void
}

/*
Expand Down Expand Up @@ -53,6 +60,14 @@ export class Web3Provider extends JsonRpcProvider {
defineReadOnly(this, "_web3Provider", web3Provider);
}

/**
* Generate a unique identifier for a JSON RPC.
*/
private static getUniqueId(): number {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
}


send(method: string, params: any): Promise<any> {

// Metamask complains about eth_sign (and on some versions hangs)
Expand All @@ -63,10 +78,12 @@ export class Web3Provider extends JsonRpcProvider {
}

return new Promise((resolve, reject) => {
const uniqueId = Web3Provider.getUniqueId();

let request = {
method: method,
params: params,
id: 42,
id: uniqueId,
jsonrpc: "2.0"
};

Expand Down
14 changes: 12 additions & 2 deletions packages/providers/web3-provider.d.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { Networkish } from "@ethersproject/networks";
import { JsonRpcProvider } from "./json-rpc-provider";
export declare type JsonRpc<TMethod = any, TParams = any> = {
jsonrpc: '2.0';
id: number;
method: TMethod;
params: TParams;
};
export declare type AsyncSendable = {
isMetaMask?: boolean;
host?: string;
path?: string;
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void;
send?: (request: any, callback: (error: any, response: any) => void) => void;
sendAsync?: (request: JsonRpc, callback: (error: any, response: any) => void) => void;
send?: (request: JsonRpc, callback: (error: any, response: any) => void) => void;
};
export declare class Web3Provider extends JsonRpcProvider {
readonly _web3Provider: AsyncSendable;
private _sendAsync;
constructor(web3Provider: AsyncSendable, network?: Networkish);
/**
* Generate a unique identifier for a JSON RPC.
*/
private static getUniqueId;
send(method: string, params: any): Promise<any>;
}
9 changes: 8 additions & 1 deletion packages/providers/web3-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ var Web3Provider = /** @class */ (function (_super) {
properties_1.defineReadOnly(_this, "_web3Provider", web3Provider);
return _this;
}
/**
* Generate a unique identifier for a JSON RPC.
*/
Web3Provider.getUniqueId = function () {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
};
Web3Provider.prototype.send = function (method, params) {
var _this = this;
// Metamask complains about eth_sign (and on some versions hangs)
Expand All @@ -61,10 +67,11 @@ var Web3Provider = /** @class */ (function (_super) {
params = [params[1], params[0]];
}
return new Promise(function (resolve, reject) {
var uniqueId = Web3Provider.getUniqueId();
var request = {
method: method,
params: params,
id: 42,
id: uniqueId,
jsonrpc: "2.0"
};
_this._sendAsync(request, function (error, result) {
Expand Down