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.
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
### 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
Ethereum API methods can be sent and received:
```js
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
```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`.
@ -51,7 +67,7 @@ The event emits with `result`, the subscription `result` or an `Error` object.
#### Unsubscribe
```js
ethereum.unsubscribe(subscriptionId: String): Promise<Boolean|Error>;
ethereum.unsubscribe(subscriptionId: String): Promise<Boolean>;
```
Promise resolves with `success: Boolean` or rejects with `Error`.
@ -117,113 +133,131 @@ ethereum.constructor.name;
## 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);
const ethereum = window.ethereum;
function start(ethereum) {
// A) Primary use case - set provider in web3.js
web3.setProvider(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}`
);
});
// B) Secondary use case - use provider object directly
// Example 1: 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 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 2: Enable full provider
ethereum
.enable()
.then(accounts => {
console.log(`Enabled accounts:\n${accounts.join('\n')}`);
})
.catch(error => {
console.error(
`Error enabling provider: ${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 => {
// Example 3: Log available accounts
ethereum
.send('eth_accounts')
.then(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}`
})
.catch(error => {
console.error(
`Error fetching accounts: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
});
}
// 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
### 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
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 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 JSON-RPC request requires an account that is not yet authenticated, the Promise **MUST** reject with an `Error`.
### 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.
@ -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.
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
@ -287,6 +327,15 @@ class EthereumProvider extends EventEmitter {
/* Methods */
enable() {
return new Promise((resolve, reject) => {
window.mist
.requestAccounts()
.then(resolve)
.catch(reject);
});
}
send(method, params = []) {
if (!method || typeof method !== 'string') {
return new Error('Method is not a valid string.');