UNPKG

@prajjawal/qrl_providers

Version:

A JavaScript Ethereum provider that connects to the wallet over a stream.

296 lines 11.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetaMaskInpageProvider = exports.MetaMaskInpageProviderStreamName = void 0; const eth_rpc_errors_1 = require("eth-rpc-errors"); const siteMetadata_1 = require("./siteMetadata"); const messages_1 = __importDefault(require("./messages")); const utils_1 = require("./utils"); const StreamProvider_1 = require("./StreamProvider"); /** * The name of the stream consumed by {@link MetaMaskInpageProvider}. */ exports.MetaMaskInpageProviderStreamName = 'qrl-provider'; class MetaMaskInpageProvider extends StreamProvider_1.AbstractStreamProvider { /** * @param connectionStream - A Node.js duplex stream * @param options - An options bag * @param options.jsonRpcStreamName - The name of the internal JSON-RPC stream. * Default: qrl-provider * @param options.logger - The logging API to use. Default: console * @param options.maxEventListeners - The maximum number of event * listeners. Default: 100 * @param options.shouldSendMetadata - Whether the provider should * send page metadata. Default: true */ constructor(connectionStream, { jsonRpcStreamName = exports.MetaMaskInpageProviderStreamName, logger = console, maxEventListeners, shouldSendMetadata, } = {}) { super(connectionStream, { jsonRpcStreamName, logger, maxEventListeners, rpcMiddleware: utils_1.getDefaultExternalMiddleware(logger), }); this._sentWarnings = { // methods enable: false, experimentalMethods: false, send: false, // events events: { close: false, data: false, networkChanged: false, notification: false, }, }; // We shouldn't perform asynchronous work in the constructor, but at one // point we started doing so, and changing this class isn't worth it at // the time of writing. this._initializeStateAsync(); this.networkVersion = null; this.isMetaMask = true; this._sendSync = this._sendSync.bind(this); this.enable = this.enable.bind(this); this.send = this.send.bind(this); this.sendAsync = this.sendAsync.bind(this); this._warnOfDeprecation = this._warnOfDeprecation.bind(this); this._metamask = this._getExperimentalApi(); // handle JSON-RPC notifications this._jsonRpcConnection.events.on('notification', (payload) => { const { method } = payload; if (utils_1.EMITTED_NOTIFICATIONS.includes(method)) { // deprecated // emitted here because that was the original order this.emit('data', payload); // deprecated this.emit('notification', payload.params.result); } }); // send website metadata if (shouldSendMetadata) { if (document.readyState === 'complete') { siteMetadata_1.sendSiteMetadata(this._rpcEngine, this._log); } else { const domContentLoadedHandler = () => { siteMetadata_1.sendSiteMetadata(this._rpcEngine, this._log); window.removeEventListener('DOMContentLoaded', domContentLoadedHandler); }; window.addEventListener('DOMContentLoaded', domContentLoadedHandler); } } } //==================== // Public Methods //==================== /** * Submits an RPC request per the given JSON-RPC request object. * * @param payload - The RPC request object. * @param callback - The callback function. */ sendAsync(payload, callback) { this._rpcRequest(payload, callback); } /** * We override the following event methods so that we can warn consumers * about deprecated events: * addListener, on, once, prependListener, prependOnceListener */ addListener(eventName, listener) { this._warnOfDeprecation(eventName); return super.addListener(eventName, listener); } on(eventName, listener) { this._warnOfDeprecation(eventName); return super.on(eventName, listener); } once(eventName, listener) { this._warnOfDeprecation(eventName); return super.once(eventName, listener); } prependListener(eventName, listener) { this._warnOfDeprecation(eventName); return super.prependListener(eventName, listener); } prependOnceListener(eventName, listener) { this._warnOfDeprecation(eventName); return super.prependOnceListener(eventName, listener); } //==================== // Private Methods //==================== /** * When the provider becomes disconnected, updates internal state and emits * required events. Idempotent with respect to the isRecoverable parameter. * * Error codes per the CloseEvent status codes as required by EIP-1193: * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes * * @param isRecoverable - Whether the disconnection is recoverable. * @param errorMessage - A custom error message. * @emits BaseProvider#disconnect */ _handleDisconnect(isRecoverable, errorMessage) { super._handleDisconnect(isRecoverable, errorMessage); if (this.networkVersion && !isRecoverable) { this.networkVersion = null; } } /** * Warns of deprecation for the given event, if applicable. */ _warnOfDeprecation(eventName) { var _a; if (((_a = this._sentWarnings) === null || _a === void 0 ? void 0 : _a.events[eventName]) === false) { this._log.warn(messages_1.default.warnings.events[eventName]); this._sentWarnings.events[eventName] = true; } } //==================== // Deprecated Methods //==================== /** * Equivalent to: ethereum.request('eth_requestAccounts') * * @deprecated Use request({ method: 'eth_requestAccounts' }) instead. * @returns A promise that resolves to an array of addresses. */ enable() { if (!this._sentWarnings.enable) { this._log.warn(messages_1.default.warnings.enableDeprecation); this._sentWarnings.enable = true; } return new Promise((resolve, reject) => { try { this._rpcRequest({ method: 'eth_requestAccounts', params: [] }, utils_1.getRpcPromiseCallback(resolve, reject)); } catch (error) { reject(error); } }); } send(methodOrPayload, callbackOrArgs) { if (!this._sentWarnings.send) { this._log.warn(messages_1.default.warnings.sendDeprecation); this._sentWarnings.send = true; } if (typeof methodOrPayload === 'string' && (!callbackOrArgs || Array.isArray(callbackOrArgs))) { return new Promise((resolve, reject) => { try { this._rpcRequest({ method: methodOrPayload, params: callbackOrArgs }, utils_1.getRpcPromiseCallback(resolve, reject, false)); } catch (error) { reject(error); } }); } else if (methodOrPayload && typeof methodOrPayload === 'object' && typeof callbackOrArgs === 'function') { return this._rpcRequest(methodOrPayload, callbackOrArgs); } return this._sendSync(methodOrPayload); } /** * Internal backwards compatibility method, used in send. * * @deprecated */ _sendSync(payload) { let result; switch (payload.method) { case 'eth_accounts': result = this.selectedAddress ? [this.selectedAddress] : []; break; case 'eth_coinbase': result = this.selectedAddress || null; break; case 'eth_uninstallFilter': this._rpcRequest(payload, utils_1.NOOP); result = true; break; case 'net_version': result = this.networkVersion || null; break; default: throw new Error(messages_1.default.errors.unsupportedSync(payload.method)); } return { id: payload.id, jsonrpc: payload.jsonrpc, result, }; } /** * Constructor helper. * * Gets the experimental _metamask API as Proxy, so that we can warn consumers * about its experimental nature. */ _getExperimentalApi() { return new Proxy({ /** * Determines if MetaMask is unlocked by the user. * * @returns Promise resolving to true if MetaMask is currently unlocked */ isUnlocked: async () => { if (!this._state.initialized) { await new Promise((resolve) => { this.on('_initialized', () => resolve()); }); } return this._state.isUnlocked; }, /** * Make a batch RPC request. */ requestBatch: async (requests) => { if (!Array.isArray(requests)) { throw eth_rpc_errors_1.ethErrors.rpc.invalidRequest({ message: 'Batch requests must be made with an array of request objects.', data: requests, }); } return new Promise((resolve, reject) => { this._rpcRequest(requests, utils_1.getRpcPromiseCallback(resolve, reject)); }); }, }, { get: (obj, prop, ...args) => { if (!this._sentWarnings.experimentalMethods) { this._log.warn(messages_1.default.warnings.experimentalMethods); this._sentWarnings.experimentalMethods = true; } return Reflect.get(obj, prop, ...args); }, }); } /** * Upon receipt of a new chainId and networkVersion, emits corresponding * events and sets relevant public state. Does nothing if neither the chainId * nor the networkVersion are different from existing values. * * @emits MetamaskInpageProvider#networkChanged * @param networkInfo - An object with network info. * @param networkInfo.chainId - The latest chain ID. * @param networkInfo.networkVersion - The latest network ID. */ _handleChainChanged({ chainId, networkVersion, } = {}) { // This will validate the params and disconnect the provider if the // networkVersion is 'loading'. super._handleChainChanged({ chainId, networkVersion }); if (this._state.isConnected && networkVersion !== this.networkVersion) { this.networkVersion = networkVersion; if (this._state.initialized) { this.emit('networkChanged', this.networkVersion); } } } } exports.MetaMaskInpageProvider = MetaMaskInpageProvider; //# sourceMappingURL=MetaMaskInpageProvider.js.map