EIPs/EIPS/eip-1193.md
Ryan Ghods 5508f14208 Automatically merged updates to draft EIP(s) 1193
Hi, I'm a bot! This change was automatically merged because:

 - It only modifies existing Draft or Last Call EIP(s)
 - The PR was approved or written by at least one author of each modified EIP
 - The build is passing
2018-07-22 18:13:16 +01:00

260 lines
9.9 KiB
Markdown

---
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<any>): Promise<any>;
```
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<any>): Promise<any>;
```
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<Boolean|Error>;
```
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<String>) => 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<any>}]` 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<String>` 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/).