mirror of https://github.com/status-im/EIPs.git
Merge branch 'master' into patch-1
This commit is contained in:
commit
2038638bfb
227
EIPS/eip-1193.md
227
EIPS/eip-1193.md
|
@ -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.');
|
||||
|
|
Loading…
Reference in New Issue