mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-26 13:45:22 +00:00
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
This commit is contained in:
parent
153bce3091
commit
a8e18356ea
211
EIPS/eip-1193.md
211
EIPS/eip-1193.md
@ -2,7 +2,7 @@
|
||||
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
|
||||
discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider-javascript-api/640
|
||||
status: Draft
|
||||
type: Standards Track
|
||||
category: Interface
|
||||
@ -33,7 +33,7 @@ See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json-
|
||||
#### Subscribe
|
||||
|
||||
```js
|
||||
ethereum.subscribe(subscriptionType: String, params?: Array<any>): Promise<any>;
|
||||
ethereum.subscribe(subscriptionType: String, params?: Array<any>): Promise<String|Error>;
|
||||
```
|
||||
|
||||
Promise resolves with `subscriptionId: String` or rejects with `Error`.
|
||||
@ -220,23 +220,23 @@ If the Ethereum JSON-RPC API returns a response object with no error, then the P
|
||||
|
||||
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 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 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.
|
||||
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).
|
||||
|
||||
### 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 `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 `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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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 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`.
|
||||
|
||||
If the implementing provider does not support subscriptions, then it **MUST** leave the `subscribe` and `unsubscribe` methods undefined.
|
||||
|
||||
@ -244,9 +244,9 @@ If the implementing provider does not support subscriptions, then it **MUST** le
|
||||
|
||||
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 connection closes, the Ethereum Provider **MUST** emit an event named `close` with args `code: Number, reason: String` following the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes).
|
||||
|
||||
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 network the provider is connected to changes, the 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).
|
||||
|
||||
@ -254,6 +254,197 @@ If the accounts connected to the Ethereum Provider change, the Ethereum Provider
|
||||
|
||||
The name of the constructor of the Ethereum Provider **MUST** be `EthereumProvider`.
|
||||
|
||||
### web3.js Provider
|
||||
|
||||
The implementing Ethereum Provider **MUST** be compatible as a `web3.js` provider. This is accomplished by providing two methods in the `EthereumProvider`: `sendAsync(payload: Object, callback: (error: any, result: any) => void): void` and `isConnected(): Boolean`.
|
||||
|
||||
### Error object and codes
|
||||
|
||||
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).
|
||||
|
||||
## Example Class Implementation
|
||||
|
||||
```js
|
||||
class EthereumProvider extends EventEmitter {
|
||||
constructor() {
|
||||
// Call super for `this` to be defined
|
||||
super();
|
||||
|
||||
// Init storage
|
||||
this._isConnected = false;
|
||||
this._nextJsonrpcId = 0;
|
||||
this._promises = {};
|
||||
this._activeSubscriptions = [];
|
||||
|
||||
// Fire the connect
|
||||
this._connect();
|
||||
|
||||
// Listen for jsonrpc responses
|
||||
window.addEventListener('message', this._handleJsonrpcMessage.bind(this));
|
||||
}
|
||||
|
||||
/* Methods */
|
||||
|
||||
send(method, params = []) {
|
||||
if (!method || typeof method !== 'string') {
|
||||
return new Error('Method is not a valid string.');
|
||||
}
|
||||
|
||||
if (!(params instanceof Array)) {
|
||||
return new Error('Params is not a valid array.');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
id: this._nextJsonrpcId++,
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
params
|
||||
};
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this._promises[payload.id] = { resolve, reject };
|
||||
});
|
||||
|
||||
// Send jsonrpc request to node
|
||||
window.postMessage({ type: 'write', message: payload }, origin);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
subscribe(subscriptionType, params) {
|
||||
return this.send('eth_subscribe', [subscriptionType, ...params]).then(
|
||||
subscriptionId => {
|
||||
this._activeSubscriptions.push(subscriptionId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
unsubscribe(subscriptionId) {
|
||||
return this.send('eth_unsubscribe', [subscriptionId]).then(success => {
|
||||
if (success) {
|
||||
// Remove subscription
|
||||
this._activeSubscription = this._activeSubscription.filter(
|
||||
id => id !== subscriptionId
|
||||
);
|
||||
// Remove listeners on subscriptionId
|
||||
this.removeAllListeners(subscriptionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Internal methods */
|
||||
|
||||
_handleJsonrpcMessage(event) {
|
||||
// Return if no data to parse
|
||||
if (!event || !event.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(event.data);
|
||||
} catch (error) {
|
||||
// Return if we can't parse a valid object
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if not a jsonrpc response
|
||||
if (!data || !data.message || !data.message.jsonrpc) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = data.message;
|
||||
const { id, method, error, result } = message;
|
||||
|
||||
if (typeof id !== 'undefined') {
|
||||
const promise = this._promises[id];
|
||||
if (promise) {
|
||||
// Handle pending promise
|
||||
if (data.type === 'error') {
|
||||
promise.reject(message);
|
||||
} else if (message.error) {
|
||||
promise.reject(error);
|
||||
} else {
|
||||
promise.resolve(result);
|
||||
}
|
||||
delete this.promises[id];
|
||||
}
|
||||
} else {
|
||||
if (method && method.indexOf('_subscription') > -1) {
|
||||
// Emit subscription result
|
||||
const { subscription, result } = message.params;
|
||||
this.emit(subscription, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connection handling */
|
||||
|
||||
_connect() {
|
||||
// Send to node
|
||||
window.postMessage({ type: 'create' }, origin);
|
||||
|
||||
// Reconnect on close
|
||||
this.once('close', this._connect.bind(this));
|
||||
}
|
||||
|
||||
/* Events */
|
||||
|
||||
_emitConnect() {
|
||||
this._isConnected = true;
|
||||
this.emit('connect');
|
||||
}
|
||||
|
||||
_emitClose(code, reason) {
|
||||
this._isConnected = false;
|
||||
this.emit('close', code, reason);
|
||||
|
||||
// Send Error objects to any open subscriptions
|
||||
this._activeSubscriptions.forEach(id => {
|
||||
const error = new Error(
|
||||
`Provider connection to network closed.
|
||||
Subscription lost, please subscribe again.`
|
||||
);
|
||||
this.emit(id, error);
|
||||
});
|
||||
// Clear subscriptions
|
||||
this._activeSubscriptions = [];
|
||||
}
|
||||
|
||||
_emitNetworkChanged(networkId) {
|
||||
this.emit('networkChanged', networkId);
|
||||
}
|
||||
|
||||
_emitAccountsChanged(accounts) {
|
||||
this.emit('accountsChanged', accounts);
|
||||
}
|
||||
|
||||
/* web3.js provider compatibility */
|
||||
|
||||
sendAsync(payload, callback) {
|
||||
return this.send(payload.method, payload.params)
|
||||
.then(result => {
|
||||
const response = payload;
|
||||
response.result = result;
|
||||
callback(null, response);
|
||||
})
|
||||
.catch(error => {
|
||||
callback(error, null);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`Error from EthereumProvider sendAsync ${payload}: ${error}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this._isConnected;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
Loading…
x
Reference in New Issue
Block a user