EIPs

EIP-1193 — Ethereum Provider JavaScript API

eips.ethereum.org/EIPS/eip-1193

The Provider envelope every wallet implements. A Provider is a four-field object:

import type {
  EventMap,
  EventName,
  Provider,
  RequestArguments,
} from "@ethernauta/eip/1193";

// `Provider` is structurally:
//   request:        (args: RequestArguments) => Promise<unknown>
//   on:             <E>(event: E, listener: (payload: EventMap[E]) => void) => void
//   removeListener: <E>(event: E, listener: (payload: EventMap[E]) => void) => void
//   emit:           <E>(event: E, payload: EventMap[E]) => void
declare const example: Provider;
const args: RequestArguments = { method: "eth_chainId" };
const events: EventName = "accountsChanged";
const payload: EventMap["accountsChanged"] = [];
void example;
void args;
void events;
void payload;

That’s all. Method routing, allowlists, caching, confirmation policy — none of that is part of 1193. See Concepts → 1193 is a transport.

import { create_provider } from "@ethernauta/eip/1193";

declare function dispatch(method: string, params: readonly unknown[] | object | undefined): Promise<unknown>;

const provider = create_provider({
  request: async ({ method, params }) => dispatch(method, params),
});

Surface

Provider

ExportTypePurpose
create_provider(options) => ProviderBuild a 1193 provider with the four-field shape.
Provider, RequestArguments, CreateProviderOptionstypesInferred shapes.
RequestArgumentsSchemaValibot schemaValidate incoming request calls.

Errors

EIP-1193 defines a standard error space (4001 user rejected, 4100 unauthorized, 4200 unsupported method, 4900/4901 disconnected, …):

ExportCodePurpose
ERROR_CODEenum-likeAll standard codes by name.
invalid_params-32602Bad params.
unauthorized4100Not authorized by user.
user_rejected4001User clicked “reject.”
unsupported_method4200Method not implemented.
unrecognized_chain4902Chain not added to wallet.
chain_disconnected4901Disconnected from chain.
provider_error-32603Internal provider error.
disconnected4900No connection.
ErrorCodetypeDiscriminated union of the above.

Events

import { create_emitter } from "@ethernauta/eip/1193";

const emitter = create_emitter();

emitter.on("accountsChanged", (accounts) => {
  console.log(accounts);
});
emitter.on("chainChanged", (chain_id) => {
  console.log(chain_id);
});

const new_accounts: string[] = [];
emitter.emit("accountsChanged", new_accounts);
EventPayload
connect{ chain_id }
disconnectProviderRpcError
accountsChangedAddress[]
chainChangedUint (decimal chain ID as bigint)
messageEthSubscription \| ...
ExportPurpose
create_emitterBuild an event emitter conforming to 1193 semantics.
Emitter, EventMap, EventName, EthSubscription, ProviderMessage, ProviderConnectInfotypes

Convenience watchers

import { watch_accounts, watch_chain } from "@ethernauta/eip/1193";
import type { Provider } from "@ethernauta/eip/1193";

declare const provider: Provider;

const unsubscribe_accounts = watch_accounts(provider, (accounts) => {
  console.log("now exposed:", accounts);
});

const unsubscribe_chain = watch_chain(provider, (chain_id) => {
  console.log("active chain:", chain_id);
});

void unsubscribe_accounts;
void unsubscribe_chain;

Return unsubscribe functions; cleanly handle accountsChanged / chainChanged together with initial-state fetching.

Consumer side — turning a 1193 provider into resolvers

The library’s typical dapp pattern doesn’t request directly; it adapts the provider into resolver shapes:

import { create_provider } from "@ethernauta/transport";
import { eth_blockNumber, eth_sendTransaction } from "@ethernauta/eth";
import type { Provider } from "@ethernauta/eip/1193";
import { AddressSchema, BytesSchema, UintSchema } from "@ethernauta/core";
import { parse } from "valibot";

declare const injected: Provider;
const provider = create_provider(injected);

const to = parse(AddressSchema, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
const value = parse(UintSchema, "0x0");
const input = parse(BytesSchema, "0x");

const block = await eth_blockNumber()(provider.reader({ chain_id: "eip155:1" }));
const hash = await eth_sendTransaction([{ to, value, input }])(
  provider.signer({ chain_id: "eip155:1" }),
);

create_provider in @ethernauta/eip/1193 builds a provider (the wallet side, producing the four-field envelope). create_provider in @ethernauta/transport consumes a provider (the dapp side, adapting it into resolver shapes). Same name, opposite sides of the same envelope — pick the import by what your code is doing.

See also