UNPKG

@fordefi/web3-provider

Version:

Web3 Provider and signer compatible with EIP-1193

292 lines (284 loc) 11.2 kB
import { Address, RpcSchema, Quantity, Hash, Hex, TypedDataDefinition, EIP1474Methods, EIP1193EventMap, TransactionRequest, EIP1193Provider, EIP1193Events } from 'viem'; export { EIP1193EventMap } from 'viem'; import { EventEmitter } from 'node:events'; type EvmChainId = number; type EvmChainUniqueId = string; /** * Configuration for constructing a provider instance. * * Each instance manages a single address in a single chain. */ interface FordefiProviderConfig { /** * Chain ID as a number or a named chain. * * For example, assuming Ethereum Sepolia chain: * - Numeric value: 11155111. * - Named chain: 'evm_ethereum_sepolia'. */ chainId: EvmChainId | EvmChainUniqueId; /** * EVM address of a Fordefi vault. * * For example: '0x1234567890123456789012345678901234567890'. */ address: Address; /** * Fordefi API user token (base64 encoded) issued via the [Fordefi Web Console](https://app.fordefi.com/user-management). * * For example: 'eyJWthEAdEr.eyJwTPaYLoad.SiGNAtUrEBase64==' */ apiUserToken: string; /** * Private key in PEM format used to sign the body of requests sent to the Fordefi API. * This is the content of the private `.pem` file. See [Create a public/private signature key pair for the API client](https://docs.fordefi.com/reference/pair-an-api-client-with-the-api-signer). * * Example of a private ECDSA `.pem` file: * ``` * -----BEGIN EC PRIVATE KEY----- * PrivateKeyBase64== * -----END EC PRIVATE KEY----- * ``` */ apiPayloadSignKey: string; /** * Fallback JSON-RPC HTTP node URL. * Methods not implemented by this provider will pass through and be handled by this node. * * For example: 'https://rpc.sepolia.org'. */ rpcUrl?: string; /** * Fordefi API base URL (used for development). Defaults to production API URL. * * For example: 'https://api.fordefi.com'. */ apiBaseUrl?: string; /** * Whether to skip running a simulation before creating a new transaction, it blocks transaction creation if it fails. Defaults to true. */ skipPrediction?: boolean; /** * Total duration in ms of polling for transaction lifecycle status changed after a transaction was submitted. By default, it would stop after 24 hours. */ timeoutDurationMs?: number; /** * Duration in ms between checks for transaction lifecycle status updates after a transaction was submitted. The minimum value is 500, and by default it's 5000. */ pollingIntervalMs?: number; } type OmitFromArray<T extends RpcSchema, Condition extends T[number]['Method']> = { [K in keyof T]: T[K] extends { Method: Condition; } ? never : { Method: T[K]['Method']; Parameters: T[K]['Parameters']; ReturnType: T[K]['ReturnType']; }; } extends infer R ? { [K in keyof R]: R[K]; } : never; type FordefiWeb3TransactionRequest = TransactionRequest<bigint | Hex>; /** Spec of all methods implemented by Fordefi. */ type FordefiRpcSchema = readonly [ { Method: 'eth_chainId'; Parameters?: undefined; ReturnType: Quantity; }, { Method: 'eth_accounts'; Parameters?: undefined; ReturnType: Address[]; }, { Method: 'eth_requestAccounts'; Parameters?: undefined; ReturnType: Address[]; }, { Method: 'eth_sendTransaction'; Parameters: [transaction: FordefiWeb3TransactionRequest]; ReturnType: Hash; }, { Method: 'eth_signTransaction'; Parameters: [request: FordefiWeb3TransactionRequest]; ReturnType: Hex; }, { Method: 'personal_sign'; Parameters: [data: Hex, address: Address]; ReturnType: Hex; }, { Method: 'eth_signTypedData'; Parameters: [address: Address, message: TypedDataDefinition]; ReturnType: Hex; }, { Method: 'eth_signTypedData_v3'; Parameters: [address: Address, message: TypedDataDefinition]; ReturnType: Hex; }, { Method: 'eth_signTypedData_v4'; Parameters: [address: Address, message: TypedDataDefinition]; ReturnType: Hex; } ]; /** Union of names of all methods implemented by Fordefi. */ type FordefiMethodName = FordefiRpcSchema[number]['Method']; /** @interface */ type NonFordefiRpcSchema = OmitFromArray<EIP1474Methods, 'eth_chainId' | 'eth_accounts' | 'eth_requestAccounts' | 'eth_sendTransaction' | 'eth_signTransaction' | 'personal_sign'>; /** Method name and its arguments. Similar to `RequestArgs<S, M>` but doesn't support unknown methods. */ type MethodArgs<S extends RpcSchema, M extends S[number]['Method']> = S extends FordefiRpcSchema ? { method: M; } & (Extract<S[number], { Method: M; Parameters?: undefined; }> extends never ? { params: Extract<S[number], { Method: M; }>['Parameters']; } : { params?: never; }) : { params?: never; }; /** * Arguments for the `request()` method. * Fordefi methods and other EIP-1474 methods are typed, other methods are treated as having unknown parameters and return type. * * @template S A schema of supported methods. * Each method's schema is represented with `Method, Params, ReturnType`. * Fordefi methods are available in {@link FordefiRpcSchema}, other EIP-1474 methods are available in {@link NonFordefiRpcSchema}. * @template M The method name as defined in spec's `Method`. * @interface */ type RequestArgs<S extends RpcSchema = RpcSchema, M extends S[number]['Method'] | unknown = unknown> = S extends FordefiRpcSchema ? M extends S[number]['Method'] ? MethodArgs<S, M> : { method: string; params?: unknown | undefined; } : { method: string; params?: unknown | undefined; }; /** * The return type of a method. * @internal */ type MethodReturnType<S extends RpcSchema = FordefiRpcSchema, M extends S[number]['Method'] | unknown = S[number]['Method'] | unknown> = M extends S[number]['Method'] ? Extract<S[number], { Method: M; }> extends never ? unknown : Extract<S[number], { Method: M; }>['ReturnType'] : unknown; /** The EIP-1193 interface the provider implements based on the spec. */ type FordefiEIP1193Provider = Omit<EIP1193Provider, 'request'> & { request<S extends RpcSchema = FordefiRpcSchema, M extends S[number]['Method'] = S[number]['Method']>(args: RequestArgs<S, M>): Promise<MethodReturnType<S, M>>; }; /** @internal */ type EIP1193EventCallbackParams = { [K in keyof EIP1193EventMap]: Parameters<EIP1193EventMap[K]>; }; /** Current state of the provider - consistent with the emitted 'connect' and 'disconnect' events. */ type ConnectivityStatus = 'connected' | 'connecting' | 'disconnected'; /** * Helper to promisify waiting for given emitter's emitted event * * @param eventEmitter - instance of an {@link EventEmitter} */ declare const waitForEmittedEvent: (eventEmitter: EventEmitter) => <Event extends keyof EIP1193EventMap>(event: Event, timeoutMs?: number) => Promise<Parameters<EIP1193EventMap[Event]>[number]>; /** * Web3 provider that implements [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193). * * The provider automatically connects to Fordefi when a new instance is constructed, and emits a `connect` event once * both `chainId` and `address` were verified: chain is supported and address is managed by the given API user. * - To subscribe to [events](https://eips.ethereum.org/EIPS/eip-1193#events-1) pass a callback to `on('eventName', callbackFn)`. * - To get instead a promise that resolves once the event is emitted use the promisified helper `waitForEmittedEvent('eventName')`. * * NOTICE: * Make sure to subscribe to 'connect' immediately after creating a new instance, and before initiating any other * async operations, to avoid a race condition where the event is emitted before the listener is attached. * * For example: * ```ts * const provider = new FordefiWeb3Provider(config) * * const onConnect = ({ chainId }: ProviderConnectInfo) => { * console.log(`Connected to chain ${chainId}`) * } * * // option 1: subscribe with a callback * provider.on('connect', onConnect) * * // option 2: act once a promise resolves * const result = await provider.waitForEmittedEvent('connect') * onConnect(result) * // or * provider * .waitForEmittedEvent('connect') * .then(onConnect) * ``` * * Emitted events: * - `connect` - provider becomes connected. * - `disconnect` - provider becomes disconnected. * - `chainChanged` - emitted once during connection with the `chainId` you provided. * - `accountsChanged` - emitted once during connection with the `address` you provided. * * Interfaces of each event callback are described in {@link EIP1193EventCallbackParams} and [EIP1193EventMap](https://github.com/wevm/viem/blob/viem%402.9.29/src/types/eip1193.ts#L61-L67). * */ declare class FordefiWeb3Provider implements FordefiEIP1193Provider { readonly on: EIP1193Events['on']; readonly removeListener: EIP1193Events['removeListener']; readonly waitForEmittedEvent: ReturnType<typeof waitForEmittedEvent>; private readonly eventEmitter; private readonly apiClient; private readonly config; private chain; private vault; private status; constructor(config: FordefiProviderConfig); private validateConfig; /** * Handles a JSON-RPC request * * @param args JSON-RPC request payload * @returns A promise with a response * * @throws RpcError with error codes defined in {@link RpcErrorCode} and {@link ProviderRpcErrorCode} */ request<S extends RpcSchema = FordefiRpcSchema | NonFordefiRpcSchema, M extends S[number]['Method'] = S[number]['Method']>(args: RequestArgs<S, M>): Promise<MethodReturnType<S, M>>; private jsonRpcHttpRequestFn; private ethChainId; private ethAccounts; private ethSendTransaction; private ethSignTransaction; private personalSign; private ethSignTypedData; private _createEvmMessageTransaction; private _invokeCreateTransaction; private _getFordefiChainVault; private _throwIfNotConnected; /** * Connects the provider to Fordefi and emits a 'connect' event. * - If already connected, it does nothing. * - If connecting, waits for the connection to be established. * * @returns A promise that resolves once connected. */ connect(): Promise<void>; /** * Disconnects the provider and emits a 'disconnect' event. */ disconnect(): void; private _onDisconnect; /** * Returns the current status of the provider. * * @returns 'connected' if the provider is connected, 'connecting' if trying to connect, 'disconnected' otherwise. */ getStatus(): ConnectivityStatus; } export { type ConnectivityStatus, type EIP1193EventCallbackParams, type FordefiMethodName, type FordefiProviderConfig, type FordefiRpcSchema, FordefiWeb3Provider, type MethodReturnType, type NonFordefiRpcSchema, type RequestArgs };