Skip to content

Commit

Permalink
Automatically merged updates to draft EIP(s) 1193
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ryanio authored and eip-automerger committed Sep 10, 2018
1 parent cc61e82 commit a3384c8
Showing 1 changed file with 140 additions and 91 deletions.
231 changes: 140 additions & 91 deletions EIPS/eip-1193.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
```
Expand All @@ -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`.
Expand All @@ -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`.
Expand Down Expand Up @@ -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);

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}`
);
});
const ethereum = window.ethereum;

// A) Primary use case - set provider in web3.js
web3.setProvider(ethereum);

// 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 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 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 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.
Expand Down Expand Up @@ -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

Expand All @@ -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.');
Expand Down

0 comments on commit a3384c8

Please sign in to comment.