mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-23 20:28:21 +00:00
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
499 lines
19 KiB
Markdown
499 lines
19 KiB
Markdown
---
|
|
eip: 1193
|
|
title: Ethereum Provider JavaScript API
|
|
author: Fabian Vogelsteller (@frozeman), Ryan Ghods (@ryanio), Victor Maia (@MaiaVictor), Marc Garreau (@marcgarreau), Erik Marks (@rekmarks)
|
|
discussions-to: https://github.com/ethereum/EIPs/issues/2319
|
|
status: Draft
|
|
type: Standards Track
|
|
category: Interface
|
|
created: 2018-06-30
|
|
requires: 155, 695, 1102, 1474, 1767
|
|
---
|
|
|
|
## Summary
|
|
|
|
This EIP formalizes a JavaScript Ethereum Provider API for consistency across clients and applications.
|
|
|
|
The Provider's interface is designed to be minimal, preferring that features are introduced in the API layer (e.g. see [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102)), and agnostic of transport and RPC protocols.
|
|
|
|
Historically, Providers have been made available as `window.ethereum` in web browsers, but this convention is not part of the specification.
|
|
|
|
The events `connect`, `disconnect`, `chainChanged`, and `accountsChanged` are provided as a convenience to help enable reactive dapp UIs.
|
|
|
|
## API
|
|
|
|
### request
|
|
|
|
Makes an Ethereum RPC method call.
|
|
|
|
```typescript
|
|
interface RequestArguments {
|
|
method: string;
|
|
params?: unknown;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
Provider.request(args: RequestArguments): Promise<unknown>;
|
|
```
|
|
|
|
The returned Promise resolves with the method's result or rejects with a [`ProviderRpcError`](#errors). For example:
|
|
|
|
```javascript
|
|
Provider.request({ method: 'eth_accounts' })
|
|
.then((accounts) => console.log(accounts))
|
|
.catch((error) => console.error(error));
|
|
```
|
|
|
|
Consult each Ethereum RPC method's documentation for its `params` and return type.
|
|
You can find a list of common methods [here](https://eips.ethereum.org/EIPS/eip-1474).
|
|
|
|
#### RPC Protocols
|
|
|
|
Multiple RPC protocols may be available.
|
|
|
|
[EIP 1474](https://eips.ethereum.org/EIPS/eip-1474) specifies the Ethereum JSON-RPC API.
|
|
|
|
[EIP 1767](https://eips.ethereum.org/EIPS/eip-1767) specifies the Ethereum GraphQL schema.
|
|
|
|
### sendAsync (DEPRECATED)
|
|
|
|
This method is deprecated in favor of [`request`](#request).
|
|
|
|
`sendAsync` is like `request`, but with JSON-RPC objects and a callback.
|
|
|
|
```typescript
|
|
Provider.sendAsync(request: Object, callback: Function): void;
|
|
```
|
|
|
|
The interfaces of request and response objects are not specified here.
|
|
Historically, they have followed the [Ethereum JSON-RPC specification](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md).
|
|
|
|
### send (DEPRECATED)
|
|
|
|
This method is deprecated in favor of [`request`](#request).
|
|
|
|
```typescript
|
|
Provider.send(...args: Array<unknown>): unknown;
|
|
```
|
|
|
|
### Events
|
|
|
|
Events follow the [Node.js `EventEmitter`](https://nodejs.org/api/events.html) API.
|
|
|
|
#### connect
|
|
|
|
The Provider emits `connect` when it:
|
|
|
|
- first connects to a chain after being initialized.
|
|
- first connects to a chain, after the `disconnect` event was emitted.
|
|
|
|
```typescript
|
|
interface ProviderConnectInfo {
|
|
chainId: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
Provider.on('connect', listener: (connectInfo: ProviderConnectInfo) => void): Provider;
|
|
```
|
|
|
|
The event emits an object with a hexadecimal string `chainId` per the `eth_chainId` Ethereum RPC method, and other properties as determined by the Provider.
|
|
|
|
#### disconnect
|
|
|
|
The Provider emits `disconnect` when it becomes disconnected from all chains.
|
|
|
|
```typescript
|
|
Provider.on('disconnect', listener: (error: ProviderRpcError) => void): Provider;
|
|
```
|
|
|
|
This event emits a [`ProviderRpcError`](#errors). The error `code` follows the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes).
|
|
|
|
#### close (DEPRECATED)
|
|
|
|
This event is deprecated in favor of [`disconnect`](#disconnect).
|
|
|
|
#### chainChanged
|
|
|
|
The Provider emits `chainChanged` when connecting to a new chain.
|
|
|
|
```typescript
|
|
Provider.on('chainChanged', listener: (chainId: string) => void): Provider;
|
|
```
|
|
|
|
The event emits a hexadecimal string `chainId` per the `eth_chainId` Ethereum RPC method.
|
|
|
|
#### networkChanged (DEPRECATED)
|
|
|
|
The event `networkChanged` is deprecated in favor of [`chainChanged`](#chainchanged).
|
|
|
|
For details, see [EIP 155: Simple replay attack protection](https://eips.ethereum.org/EIPS/eip-155) and [EIP 695: Create eth_chainId method for JSON-RPC](https://eips.ethereum.org/EIPS/eip-695).
|
|
|
|
#### accountsChanged
|
|
|
|
The Provider emits `accountsChanged` if the accounts returned from the Provider (`eth_accounts`) change.
|
|
|
|
```typescript
|
|
Provider.on('accountsChanged', listener: (accounts: Array<string>) => void): Provider;
|
|
```
|
|
|
|
The event emits with `accounts`, an array of account addresses, per the `eth_accounts` Ethereum RPC method.
|
|
|
|
#### message
|
|
|
|
The Provider emits `message` to communicate arbitrary messages to the consumer.
|
|
Messages may include JSON-RPC notifications, GraphQL subscriptions, and/or any other event as defined by the Provider.
|
|
|
|
```typescript
|
|
interface ProviderMessage {
|
|
type: string;
|
|
data: unknown;
|
|
}
|
|
|
|
Provider.on('message', listener: (message: ProviderMessage) => void): Provider;
|
|
```
|
|
|
|
##### Subscriptions
|
|
|
|
Some clients like [Geth](https://geth.ethereum.org/docs/rpc/pubsub) and [Parity](https://wiki.parity.io/JSONRPC-eth_pubsub-module) support Publish-Subscribe (_Pub-Sub_) using JSON-RPC notifications. This lets you subscribe and wait for events instead of polling for them.
|
|
See the [`eth_` subscription methods](https://geth.ethereum.org/docs/rpc/pubsub) and [`shh_` subscription methods](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_subscribe) for details.
|
|
|
|
For e.g. `eth_subscribe` subscription updates, `ProviderMessage.type` will equal the string `'eth_subscription'`.
|
|
|
|
#### notification (DEPRECATED)
|
|
|
|
This event is deprecated in favor of [`message`](#message).
|
|
|
|
Historically, this event has returned e.g. `eth_subscribe` subscription updates of the form `{ subscription: string, result: unknown }`.
|
|
|
|
### Errors
|
|
|
|
```typescript
|
|
interface ProviderRpcError extends Error {
|
|
message: string;
|
|
code: number;
|
|
data?: unknown;
|
|
}
|
|
```
|
|
|
|
## Examples
|
|
|
|
> These examples assume a web browser environment.
|
|
|
|
```javascript
|
|
// The Provider will usually be available as window.ethereum on page load.
|
|
// This is only a convention, not a standard, and may not be the case in practice.
|
|
// Please consult the Provider implementation's documentation.
|
|
const ethereum = window.ethereum;
|
|
|
|
// A) Set Provider in web3.js
|
|
var web3 = new Web3(ethereum);
|
|
// web3.eth.getBlock('latest', true).then(...)
|
|
|
|
// B) Use Provider object directly
|
|
// Example 1: Log chainId
|
|
ethereum
|
|
.request({ method: 'eth_chainId' })
|
|
.then((chainId) => {
|
|
console.log(`hexadecimal string: ${chainId}`);
|
|
console.log(`decimal number: ${parseInt(chainId, 16)}`);
|
|
})
|
|
.catch((error) => {
|
|
console.error(`Error fetching chainId: ${error.code}: ${error.message}`);
|
|
});
|
|
|
|
// Example 2: Log last block
|
|
ethereum
|
|
.request({
|
|
method: 'eth_getBlockByNumber',
|
|
params: ['latest', 'true'],
|
|
})
|
|
.then((block) => {
|
|
console.log(`Block ${block.number}:`, block);
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
`Error fetching last block: ${error.message}.
|
|
Code: ${error.code}. Data: ${error.data}`
|
|
);
|
|
});
|
|
|
|
// Example 3: Log available accounts
|
|
ethereum
|
|
.request({ method: '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 4: Log new blocks
|
|
ethereum
|
|
.request({
|
|
method: 'eth_subscribe',
|
|
params: ['newHeads'],
|
|
})
|
|
.then((subscriptionId) => {
|
|
ethereum.on('message', (message) => {
|
|
if (message.type === 'eth_subscription') {
|
|
const { data } = message;
|
|
if (data.subscription === subscriptionId) {
|
|
if (typeof data.result === 'string' && data.result) {
|
|
const block = data.result;
|
|
console.log(`New block ${block.number}:`, block);
|
|
} else {
|
|
console.error(`Something went wrong: ${data.result}`);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
`Error making 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('disconnect', (code, reason) => {
|
|
console.log(`Ethereum Provider connection closed: ${reason}. Code: ${code}`);
|
|
});
|
|
```
|
|
|
|
## Specification
|
|
|
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC-2119](https://www.ietf.org/rfc/rfc2119.txt).
|
|
|
|
> Comments like this are non-normative.
|
|
|
|
### Definitions
|
|
|
|
_This section is non-normative._
|
|
|
|
- Provider
|
|
- A JavaScript object made available to a dapp, that provides access to Ethereum by means of a Client.
|
|
- Client
|
|
- An endpoint accessed by a Provider, that receives Remote Procedure Call (RPC) requests and returns their results.
|
|
- Remote Procedure Call (RPC)
|
|
- A Remote Procedure Call (RPC), is any request submitted to a Provider for some procedure that is to be processed by a Provider or its Client.
|
|
|
|
### Availability
|
|
|
|
_This section is non-normative._
|
|
|
|
How the Provider is made available to consumers is beyond the scope of this specification.
|
|
At the time of writing, there exists no specification for Provider availability, merely a convention.
|
|
This convention is described here for the benefit of dapp developers and Provider implementers.
|
|
|
|
Historically, Providers have been injected into web pages as `window.ethereum` (more generally, `globalThis.ethereum`), such that they are available on page load.
|
|
|
|
In practice, this convention does not handle some situations, including:
|
|
|
|
- Multiple Providers being injected into the same page, e.g. when the user has multiple wallets installed
|
|
- Asynchronously injected Providers, whether by choice or due to platform limitations
|
|
|
|
Provider implementers are encouraged to work with each other and with dapp developers to solve these problems until standards emerge.
|
|
|
|
### Connectivity
|
|
|
|
The Provider is said to be "connected" when it can service RPC requests to any chain.
|
|
|
|
The Provider is said to "disconnected" when it cannot service RPC requests to any chain.
|
|
|
|
> To service an RPC request, the Provider must successfully submit the request to the remote location, and receive a response.
|
|
> In other words, if the Provider is unable to communicate with its Client, for example due to network issues, the Provider is disconnected.
|
|
|
|
### API
|
|
|
|
The Provider **MUST** expose the API defined in this section. All API entities **MUST** adhere to the types and interfaces defined in this section.
|
|
|
|
The Provider **MAY** expose methods and properties not specified in this document.
|
|
|
|
#### request
|
|
|
|
> The `request` method is intended as a transport- and protocol-agnostic wrapper function for Remote Procedure Calls (RPCs).
|
|
|
|
```typescript
|
|
interface RequestArguments {
|
|
method: string;
|
|
params?: unknown;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
Provider.request(args: RequestArguments): Promise<unknown>;
|
|
```
|
|
|
|
The `request` method **MUST** send a properly formatted request to the Provider's Ethereum client.
|
|
Requests **MUST** be handled such that, for a given set of arguments, the returned Promise either resolves with a value per the RPC method's specification, or rejects with an error.
|
|
|
|
The `args` object **MAY** include properties not mentioned in this specification.
|
|
|
|
If resolved, the Promise **MUST NOT** resolve with any RPC protocol-specific response objects, unless the RPC method's return type is so defined by its specification.
|
|
|
|
If resolved, the Promise **MUST** resolve with a result per the RPC method's specification.
|
|
|
|
If the returned Promise rejects, it **MUST** reject with a `ProviderRpcError` as specified in the [RPC Errors](#rpc-errors) section below.
|
|
|
|
The returned Promise **MUST** reject if any of the following conditions are met:
|
|
|
|
- The client returns an error for the RPC request.
|
|
- If the error returned from the client is compatible with the `ProviderRpcError` interface, the Promise **MAY** reject with that error directly.
|
|
- The Provider encounters an fails for any reason.
|
|
- The request requires access to an unauthorized account, per [EIP 1102](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1102.md).
|
|
- If rejecting for this reason, the Promise rejection error `code` **MUST** be `4100`.
|
|
|
|
The returned Promise **SHOULD** reject if any of the following conditions are met:
|
|
|
|
- The Provider is disconnected.
|
|
- If rejecting for this reason, the Promise rejection error `code` **MUST** be `4900`.
|
|
- The RPC request is directed at a specific chain, and the Provider is not connected to that chain.
|
|
- If rejecting for this reason, the Promise rejection error `code` **MUST** be `4901`.
|
|
|
|
See the section [Connectivity](#connectivity) for the definitions of "connected" and "disconnected".
|
|
|
|
### Supported RPC Methods
|
|
|
|
A "supported RPC method" is any RPC method that may be called via the Provider.
|
|
|
|
All supported RPC methods **MUST** be identified by unique strings.
|
|
|
|
Providers **MAY** support whatever RPC methods required to fulfill their purpose, standardized or otherwise.
|
|
|
|
If a Provider supports a method defined in a finalized EIP, the Provider's implementation of this method **MUST** adhere to the method's specification.
|
|
|
|
If an RPC method defined in a finalized EIP is not supported, it **SHOULD** be rejected with an appropriate error per [EIP 1474](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md#error-codes).
|
|
|
|
#### RPC Errors
|
|
|
|
```typescript
|
|
interface ProviderRpcError extends Error {
|
|
message: string;
|
|
code: number;
|
|
data?: unknown;
|
|
}
|
|
```
|
|
|
|
- `message`
|
|
- **MUST** be a human-readable string
|
|
- **SHOULD** adhere to the specifications in the [Error Standards](#error-standards) section below
|
|
- `code`
|
|
- **MUST** be an integer number
|
|
- **SHOULD** adhere to the specifications in the [Error Standards](#error-standards) section below
|
|
- `data`
|
|
- **SHOULD** contain any other useful information about the error
|
|
|
|
##### Error Standards
|
|
|
|
`ProviderRpcError` codes and messages **SHOULD** follow these conventions, in order of priority:
|
|
|
|
1. The errors in the [Provider Errors](#provider-errors) section below
|
|
|
|
2. The [Ethereum JSON-RPC API error codes](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md#error-codes)
|
|
|
|
3. The [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes)
|
|
|
|
#### Provider Errors
|
|
|
|
| Status code | Name | Description |
|
|
| ----------- | --------------------- | ------------------------------------------------------------------------ |
|
|
| 4001 | User Rejected Request | The user rejected the request. |
|
|
| 4100 | Unauthorized | The requested method and/or account has not been authorized by the user. |
|
|
| 4200 | Unsupported Method | The Provider does not support the requested method. |
|
|
| 4900 | Not Connected | The Provider is not connected to any chains. |
|
|
| 4901 | Chain Not Connected | The Provider is not connected to the requested chain. |
|
|
|
|
### Events
|
|
|
|
The Provider **SHOULD** extend the [Node.js `EventEmitter`](https://nodejs.org/api/events.html) to provide dapps flexibility in listening to events. In place of full `EventEmitter` functionality, the Provider **MAY** provide as many methods as it can reasonably provide, but **MUST** provide at least `on`, `emit`, and `removeListener`.
|
|
|
|
#### message
|
|
|
|
The Provider **MAY** emit the event named `message`, for any reason.
|
|
|
|
If the Provider supports Ethereum RPC subscriptions, e.g. [`eth_subscribe`](https://geth.ethereum.org/docs/rpc/pubsub), the Provider **MUST** emit the `message` event when it receives a subscription notification.
|
|
|
|
When emitted, the `message` event **MUST** be emitted with an object argument of the following form:
|
|
|
|
```typescript
|
|
interface ProviderMessage {
|
|
type: string;
|
|
data: unknown;
|
|
}
|
|
```
|
|
|
|
##### Converting a Subscription Message to a ProviderMessage
|
|
|
|
If the Provider receives a subscription message from e.g. an `eth_subscribe` subscription, the Provider **MUST** emit a `message` event with a `ProviderMessage` object of the following form:
|
|
|
|
```typescript
|
|
interface EthSubscription extends ProviderMessage {
|
|
type: 'eth_subscription';
|
|
data: {
|
|
subscription: string;
|
|
result: unknown;
|
|
};
|
|
}
|
|
```
|
|
|
|
#### connect
|
|
|
|
See the section [Connectivity](#connectivity) for the definition of "connected".
|
|
|
|
If the Provider becomes connected, the Provider **MUST** emit the event named `connect`.
|
|
|
|
This includes when:
|
|
|
|
- The Provider first connects to a chain after initialization.
|
|
- The Provider connects to a chain after the `disconnect` event was emitted.
|
|
|
|
This event **MUST** be emitted with an object of the following form:
|
|
|
|
```typescript
|
|
interface ProviderConnectInfo {
|
|
chainId: string;
|
|
[key: string]: unknown;
|
|
}
|
|
```
|
|
|
|
`chainId` **MUST** specify the integer ID of the connected chain as a hexadecimal string, per the [`eth_chainId`](https://eips.ethereum.org/EIPS/eip-695) Ethereum RPC method.
|
|
|
|
The `ProviderConnectInfo` object **MAY** contain any other `string` properties with values of any type.
|
|
|
|
#### disconnect
|
|
|
|
See the section [Connectivity](#connectivity) for the definition of "disconnected".
|
|
|
|
If the Provider becomes disconnected from all chains, the Provider **MUST** emit the event named `disconnect` with value `error: ProviderRpcError`, per the interfaced defined in the [RPC Errors](#rpc-errors) section. The value of the error's `code` property **MUST** follow the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes).
|
|
|
|
#### chainChanged
|
|
|
|
If the chain the Provider is connected to changes, the Provider **MUST** emit the event named `chainChanged` with value `chainId: string`, specifying the integer ID of the new chain as a hexadecimal string, per the [`eth_chainId`](https://eips.ethereum.org/EIPS/eip-695) Ethereum RPC method.
|
|
|
|
#### accountsChanged
|
|
|
|
If the accounts available to the Provider change, the Provider **MUST** emit the event named `accountsChanged` with value `accounts: Array<string>`, containing the account addresses per the `eth_accounts` Ethereum RPC method.
|
|
|
|
The "accounts available to the Provider" change when the return value of `eth_accounts` changes.
|
|
|
|
## References
|
|
|
|
- [Initial discussion in `ethereum/interfaces`](https://github.com/ethereum/interfaces/issues/16)
|
|
- [Ethereum Magicians thread](https://ethereum-magicians.org/t/eip-1193-ethereum-provider-javascript-api/640)
|
|
- [Continuing EIP-1193 discussion](https://github.com/ethereum/EIPs/issues/2319)
|
|
|
|
## Copyright
|
|
|
|
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|