Merge branch 'master' into patch-1

This commit is contained in:
Nitika Goel 2018-09-10 21:18:03 +05:30 committed by GitHub
commit 2038638bfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 138 additions and 89 deletions

View File

@ -14,12 +14,28 @@ requires: 1102
This EIP formalizes an Ethereum Provider JavaScript API for consistency across clients and applications. This EIP formalizes an Ethereum Provider JavaScript API for consistency across clients and applications.
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`. The provider is designed to be minimal, containing 4 methods: `enable`, `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`.
It is intended to be available on `window.ethereum`.
## API ## API
### Enable
By default a "read-only" provider is supplied to allow access to the blockchain while preserving user privacy.
A full provider can be requested to allow account-level methods:
```js
ethereum.enable(): Promise<[String]>;
```
Promise resolves with an array of the accounts' public keys, or rejects with `Error`.
### Send ### Send
Ethereum API methods can be sent and received:
```js ```js
ethereum.send(method: String, params?: Array<any>): Promise<any>; ethereum.send(method: String, params?: Array<any>): Promise<any>;
``` ```
@ -33,7 +49,7 @@ See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json-
#### Subscribe #### Subscribe
```js ```js
ethereum.subscribe(subscriptionType: String, params?: Array<any>): Promise<String|Error>; ethereum.subscribe(subscriptionType: String, params?: Array<any>): Promise<String>;
``` ```
Promise resolves with `subscriptionId: String` or rejects with `Error`. Promise resolves with `subscriptionId: String` or rejects with `Error`.
@ -51,7 +67,7 @@ The event emits with `result`, the subscription `result` or an `Error` object.
#### Unsubscribe #### Unsubscribe
```js ```js
ethereum.unsubscribe(subscriptionId: String): Promise<Boolean|Error>; ethereum.unsubscribe(subscriptionId: String): Promise<Boolean>;
``` ```
Promise resolves with `success: Boolean` or rejects with `Error`. Promise resolves with `success: Boolean` or rejects with `Error`.
@ -117,113 +133,131 @@ ethereum.constructor.name;
## Examples ## Examples
```js ```js
// Request Ethereum Provider (EIP 1102) const ethereum = window.ethereum;
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
// A) Primary use case - set provider in web3.js web3.setProvider(ethereum);
web3.setProvider(ethereum);
// B) Secondary use case - use provider object directly // B) Secondary use case - use provider object directly
// Example: Log accounts // Example 1: Log last block
ethereum ethereum
.send('eth_accounts') .send('eth_getBlockByNumber', ['latest', 'true'])
.then(accounts => { .then(block => {
console.log(`Accounts:\n${accounts.join('\n')}`); console.log(`Block ${block.number}:\n${block}`);
}) })
.catch(error => { .catch(error => {
console.error( console.error(
`Error fetching accounts: ${error.message}. `Error fetching last block: ${error.message}.
Code: ${error.code}. Data: ${error.data}` Code: ${error.code}. Data: ${error.data}`
); );
}); });
// Example: Log last block // Example 2: Enable full provider
ethereum ethereum
.send('eth_getBlockByNumber', ['latest', 'true']) .enable()
.then(block => { .then(accounts => {
console.log(`Block ${block.number}:\n${block}`); console.log(`Enabled accounts:\n${accounts.join('\n')}`);
}) })
.catch(error => { .catch(error => {
console.error( console.error(
`Error fetching last block: ${error.message}. `Error enabling provider: ${error.message}.
Code: ${error.code}. Data: ${error.data}` Code: ${error.code}. Data: ${error.data}`
); );
}); });
// Example: Log new blocks // Example 3: Log available accounts
let subId; ethereum
ethereum .send('eth_accounts')
.subscribe('newHeads') .then(accounts => {
.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')}`); console.log(`Accounts:\n${accounts.join('\n')}`);
}; })
ethereum.on('accountsChanged', logAccounts); .catch(error => {
// to unsubscribe console.error(
ethereum.removeListener('accountsChanged', logAccounts); `Error fetching accounts: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
// Example: Log if connection ends
ethereum.on('close', (code, reason) => {
console.log(
`Ethereum provider connection closed: ${reason}. Code: ${code}`
); );
}); });
} }
// Example 4: 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 5: 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 6: Log if connection ends
ethereum.on('close', (code, reason) => {
console.log(
`Ethereum provider connection closed: ${reason}. Code: ${code}`
);
});
``` ```
## Specification ## Specification
### Enable
The provider supplied to a new dapp **MUST** be a "read-only" provider: authenticating no accounts by default, returning a blank array for `eth_accounts`, and rejecting any methods that require an account.
If the dapp has been previously authenticated and remembered by the user, then the provider supplied on load **MAY** automatically be enabled with the previously authenticated accounts.
If no accounts are authenticated, the `enable` method **MUST** ask the user which account(s) they would like to authenticate to the dapp. If the request has been previously granted and remembered, the `enable` method **MAY** immediately return with the prior remembered accounts and permissions.
The `enable` method **MUST** return a Promise, resolving with an array of the accounts' public keys, or rejecting with an `Error`. If the accounts enabled by provider change, the `accountsChanged` event **MUST** also emit.
### Send ### Send
The `send` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object). 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 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 the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** reject 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. If an error occurs during processing, such as an HTTP error or internal parsing error, then the Promise **MUST** reject with an `Error` object.
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). 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).
If the JSON-RPC request requires an account that is not yet authenticated, the Promise **MUST** reject with an `Error`.
### Subscriptions ### 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. 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.
@ -262,7 +296,13 @@ The implementing Ethereum Provider **MUST** be compatible as a `web3.js` provide
If an Error object is returned, it **MUST** contain 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 an Error object is returned, it **MUST** contain a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details.
Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes), along with the following table:
| Status code | Name | Description |
| ----------- | ------------------------- | -------------------------------------------------------------------------------------------------------------- |
| 4001 | User Denied Full Provider | User denied the enabling of the full Ethereum Provider by choosing not to authorize any accounts for the dapp. |
| | | |
| | | |
## Sample Class Implementation ## Sample Class Implementation
@ -287,6 +327,15 @@ class EthereumProvider extends EventEmitter {
/* Methods */ /* Methods */
enable() {
return new Promise((resolve, reject) => {
window.mist
.requestAccounts()
.then(resolve)
.catch(reject);
});
}
send(method, params = []) { send(method, params = []) {
if (!method || typeof method !== 'string') { if (!method || typeof method !== 'string') {
return new Error('Method is not a valid string.'); return new Error('Method is not a valid string.');