--- eip: 1193 title: Ethereum Provider JavaScript API author: Ryan Ghods (@ryanio), Marc Garreau (@marcgarreau) discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider/640 status: Draft type: Standards Track category: Interface created: 2018-06-30 requires: 1102 --- ## Summary This proposal formalizes an Ethereum Provider JavaScript API. The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. ## API ### Send ```js ethereum.send(method: String, params?: Array): Promise; ``` Promise resolves with `result` or rejects with `Error`. See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json-rpc-methods). ### Subscriptions #### Subscribe ```js ethereum.subscribe(subscriptionType: String, params?: Array): Promise; ``` Promise resolves with `subscriptionId: String` or rejects with `Error`. See the [types of subscriptions](https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB#supported-subscriptions). Results emit on `subscriptionId` using [EventEmitter](https://nodejs.org/api/events.html). Attach listeners with: ```js ethereum.on(subscriptionId, listener: (result: any) => void): this; ``` The event emits with `result`, the subscription `result` or an `Error` object. #### Unsubscribe ```js ethereum.unsubscribe(subscriptionId: String): Promise; ``` Promise resolves with `success: Boolean` or rejects with `Error`. All [EventEmitter](https://nodejs.org/api/events.html) listeners on `subscriptionId` will also be removed. ### Events Events are emitted using [EventEmitter](https://nodejs.org/api/events.html). #### connect The provider emits `connect` on connect to a network. ```js ethereum.on('connect', listener: () => void): this; ``` You can detect which network by sending `net_version`: ```js const network = await ethereum.send('net_version'); > '1' ``` #### close The provider emits `close` on disconnect from a network. ```js ethereum.on('close', listener: (code: Number, reason: String) => void): this; ``` The event emits with `code` and `reason`. The code follows the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). #### networkChanged The provider emits `networkChanged` on connect to a new network. ```js ethereum.on('networkChanged', listener: (networkId: String) => void): this; ``` The event emits with `networkId`, the new network returned from `net_version`. #### accountsChanged The provider emits `accountsChanged` if the accounts returned from the provider (`eth_accounts`) changes. ```js ethereum.on('accountsChanged', listener: (accounts: Array) => void): this; ``` The event emits with `accounts`, an array of the accounts' public keys. ### Constructor ```js ethereum.constructor.name; > 'EthereumProvider' ``` ## Examples ```js // Request Ethereum Provider (EIP 1102) window.addEventListener('message', event => { if (event.data && event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { start(window.ethereum); } }); window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, this.origin); function start(ethereum) { // A) Primary use case - set provider in web3.js web3.setProvider(ethereum); // B) Secondary use case - use provider object directly // Example: Log accounts ethereum .send('eth_accounts') .then(accounts => { console.log(`Accounts:\n${accounts.join('\n')}`); }) .catch(error => { console.error( `Error fetching accounts: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); }); // Example: Log last block ethereum .send('eth_getBlockByNumber', ['latest', 'true']) .then(block => { console.log(`Block ${block.number}:\n${block}`); }) .catch(error => { console.error( `Error fetching last block: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); }); // Example: Log new blocks let subId; ethereum .subscribe('newHeads') .then(subscriptionId => { subId = subscriptionId; ethereum.on(subscriptionId, block => { if (result instanceOf Error) { const error = result; console.error( `Error from newHeads subscription: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); } else { console.log(`New block ${block.number}:\n${block}`); } }); }) .catch(error => { console.error( `Error making newHeads subscription: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); }); // to unsubscribe ethereum .unsubscribe(subId) .then(result => { console.log(`Unsubscribed newHeads subscription ${subscriptionId}`); }) .catch(error => { console.error( `Error unsubscribing newHeads subscription: ${error.message}. Code: ${error.code}. Data: ${error.data}` ); }); // Example: Log when accounts change const logAccounts = accounts => { console.log(`Accounts:\n${accounts.join('\n')}`); }; ethereum.on('accountsChanged', logAccounts); // to unsubscribe ethereum.removeListener('accountsChanged', logAccounts); // Example: Log if connection ends ethereum.on('close', (code, reason) => { console.log( `Ethereum provider connection closed: ${reason}. Code: ${code}` ); }); } ``` ## Specification ### Send The `send` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object). If the Ethereum JSON-RPC API returns a response object with no error, then the Promise **MUST** resolve with the `response.result` object untouched by the implementing Ethereum Provider. If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** be rejected with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. If an error occurs during processing, such as an HTTP error or internal parsing error then the Promise **MUST** be rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). In case of an error, ensure that the Promise is rejected with an Error that matches the above shape. ### Subscriptions The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the `unsubscribe` method returns successfully with a `True` result, the implementing provider **MUST** remove all listeners on the `subscriptionId` using `ethereum.removeAllListeners(subscriptionId);`. If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. The implementing Ethereum Provider **MUST** emit every subscription response `result` with the eventName `subscriptionId`. If an error occurs or the network changes during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId` containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the implementing provider does not support subscriptions, then it **MUST** leave the `subscribe` and `unsubscribe` methods undefined. ### Events If the network connects, the Ethereum Provider **MUST** emit an event named `connect`. If the network connection closes, the Ethereum Provider **MUST** emit an event named `close` with args `code: Number, reason: String` using the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes) and a short human readable reason. If the network the Ethereum Provider is connected to changes, the Ethereum Provider **MUST** emit an event named `networkChanged` with args `networkId: String` containing the ID of the new network (using the Ethereum JSON-RPC call `net_version`). If the accounts connected to the Ethereum Provider change, the Ethereum Provider **MUST** send an event with the name `accountsChanged` with args `accounts: Array` containing the accounts' public key(s). ### Class The name of the constructor of the Ethereum Provider **MUST** be `EthereumProvider`. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).