@fordefi/web3-provider
Version:
Web3 Provider and signer compatible with EIP-1193
292 lines (284 loc) • 11.2 kB
TypeScript
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 };