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
19 KiB
eip | title | author | discussions-to | status | type | category | created | requires |
---|---|---|---|---|---|---|---|---|
1193 | Ethereum Provider JavaScript API | Fabian Vogelsteller (@frozeman), Ryan Ghods (@ryanio), Victor Maia (@MaiaVictor), Marc Garreau (@marcgarreau) | https://github.com/ethereum/EIPs/issues/2319 | Draft | Standards Track | Interface | 2018-06-30 | 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
), 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.
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
. For example:
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.
RPC Protocols
Multiple RPC protocols may be available.
EIP 1474 specifies the Ethereum JSON-RPC API.
EIP 1767 specifies the Ethereum GraphQL schema.
sendAsync (DEPRECATED)
This method is deprecated in favor of request
.
sendAsync
is like request
, but with JSON-RPC objects and a callback.
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.
send (DEPRECATED)
This method is deprecated in favor of request
.
Provider.send(...args: Array<unknown>): unknown;
Events
Events follow the Node.js EventEmitter
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.
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.
Provider.on('disconnect', listener: (error: ProviderRpcError) => void): Provider;
This event emits a ProviderRpcError
. The error code
follows the table of CloseEvent
status codes.
close (DEPRECATED)
This event is deprecated in favor of disconnect
.
chainChanged
The Provider emits chainChanged
when connecting to a new chain.
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
.
For details, see EIP 155: Simple replay attack protection and EIP 695: Create eth_chainId method for JSON-RPC.
accountsChanged
The Provider emits accountsChanged
if the accounts returned from the Provider (eth_accounts
) change.
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.
interface ProviderMessage {
type: string;
data: unknown;
}
Provider.on('message', listener: (message: ProviderMessage) => void): Provider;
Subscriptions
Some clients like Geth and Parity 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 and shh_
subscription methods 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
.
Historically, this event has returned e.g. eth_subscribe
subscription updates of the form { subscription: string, result: unknown }
.
Errors
interface ProviderRpcError extends Error {
message: string;
code: number;
data?: unknown;
}
Examples
These examples assume a web browser environment.
// 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.
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).
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 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.
- If the error returned from the client is compatible with the
- The Provider encounters an fails for any reason.
- The request requires access to an unauthorized account, per EIP 1102.
- If rejecting for this reason, the Promise rejection error
code
MUST be4100
.
- If rejecting for this reason, the Promise rejection error
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 be4900
.
- If rejecting for this reason, the Promise rejection error
- 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 be4901
.
- If rejecting for this reason, the Promise rejection error
See the section 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.
RPC Errors
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 section below
code
- MUST be an integer number
- SHOULD adhere to the specifications in the 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:
-
The errors in the Provider Errors section below
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
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
, 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:
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:
interface EthSubscription extends ProviderMessage {
type: 'eth_subscription';
data: {
subscription: string;
result: unknown;
};
}
connect
See the section 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:
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
Ethereum RPC method.
The ProviderConnectInfo
object MAY contain any other string
properties with values of any type.
disconnect
See the section 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 section. The value of the error's code
property MUST follow the status codes for CloseEvent
.
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
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
Copyright
Copyright and related rights waived via CC0.