EIPs/EIPS/eip-1193.md

501 lines
19 KiB
Markdown
Raw Normal View History

---
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: Last Call
review-period-end: 2020-06-04
type: Standards Track
category: Interface
created: 2018-06-30
requires: 155, 695
---
## 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 (see e.g. [`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. For examples, see:
- [EIP 1474](https://eips.ethereum.org/EIPS/eip-1474), the Ethereum JSON-RPC API
- [EIP 1767](https://eips.ethereum.org/EIPS/eip-1767), 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://eips.ethereum.org/EIPS/eip-1474).
### 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;
// 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 at least one chain.
The Provider is said to be "disconnected" when it cannot service RPC requests to any chain at all.
> 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 error or fails to process the request for any reason.
> If the Provider implements any kind of authorization logic, the authors recommend rejecting with a `4100` error in case of authorization failures.
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, but is connected to at least one other 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 a `4200` error per the [Provider Errors](#provider-errors) section below, or an appropriate error per the RPC method's specification.
#### 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. Any errors mandated by the erroring RPC method's specification
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 | Disconnected | The Provider is disconnected from all chains. |
| 4901 | Chain Disconnected | The Provider is not connected to the requested chain. |
> `4900` is intended to indicate that the Provider is disconnected from all chains, while `4901` is intended to indicate that the Provider is disconnected from a specific chain only.
> In other words, `4901` implies that the Provider is connected to other chains, just not the requested one.
### 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)
- Related EIPs
- [EIP 1102](https://eips.ethereum.org/EIPS/eip-1102)
- [EIP 1474](https://eips.ethereum.org/EIPS/eip-1474)
- [EIP 1767](https://eips.ethereum.org/EIPS/eip-1767)
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).