diff --git a/src/actions/public/call.ts b/src/actions/public/call.ts index 1f3ede9878..43489089fe 100644 --- a/src/actions/public/call.ts +++ b/src/actions/public/call.ts @@ -28,6 +28,7 @@ import { import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { RpcTransactionRequest } from '../../types/rpc.js' import type { StateOverride } from '../../types/stateOverride.js' @@ -93,6 +94,8 @@ export type CallParameters< factory?: Address | undefined /** Calldata to execute on the factory to deploy the contract. */ factoryData?: Hex | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined /** State overrides for the call. */ stateOverride?: StateOverride | undefined } & ( @@ -175,6 +178,7 @@ export async function call( maxFeePerGas, maxPriorityFeePerGas, nonce, + requestOptions, to, value, stateOverride, @@ -280,10 +284,13 @@ export async function call( return base })() - const response = await client.request({ - method: 'eth_call', - params, - }) + const response = await client.request( + { + method: 'eth_call', + params, + }, + requestOptions, + ) if (response === '0x') return { data: undefined } return { data: response } } catch (err) { diff --git a/src/actions/public/createAccessList.ts b/src/actions/public/createAccessList.ts index 2dab11d396..c2fc0e5aae 100644 --- a/src/actions/public/createAccessList.ts +++ b/src/actions/public/createAccessList.ts @@ -10,6 +10,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RpcTransactionRequest } from '../../types/rpc.js' import type { AccessList, TransactionRequest } from '../../types/transaction.js' import type { ExactPartial, Prettify, UnionOmit } from '../../types/utils.js' @@ -42,6 +43,8 @@ export type CreateAccessListParameters< > & { /** Account attached to the call (msg.sender). */ account?: Account | Address | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** The balance of the account at a block number. */ @@ -111,6 +114,7 @@ export async function createAccessList( maxFeePerBlobGas, maxFeePerGas, maxPriorityFeePerGas, + requestOptions, to, value, ...rest @@ -145,10 +149,13 @@ export async function createAccessList( 'createAccessList', ) as TransactionRequest - const response = await client.request({ - method: 'eth_createAccessList', - params: [request as ExactPartial, block], - }) + const response = await client.request( + { + method: 'eth_createAccessList', + params: [request as ExactPartial, block], + }, + requestOptions, + ) return { accessList: response.accessList, gasUsed: BigInt(response.gasUsed), diff --git a/src/actions/public/createBlockFilter.ts b/src/actions/public/createBlockFilter.ts index 955d6da1c8..651288ab18 100644 --- a/src/actions/public/createBlockFilter.ts +++ b/src/actions/public/createBlockFilter.ts @@ -2,10 +2,16 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { createFilterRequestScope } from '../../utils/filters/createFilterRequestScope.js' +export type CreateBlockFilterParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type CreateBlockFilterReturnType = Filter<'block'> export type CreateBlockFilterErrorType = RequestErrorType | ErrorType @@ -33,12 +39,16 @@ export type CreateBlockFilterErrorType = RequestErrorType | ErrorType */ export async function createBlockFilter( client: Client, + { requestOptions }: CreateBlockFilterParameters = {}, ): Promise { const getRequest = createFilterRequestScope(client, { method: 'eth_newBlockFilter', }) - const id = await client.request({ - method: 'eth_newBlockFilter', - }) + const id = await client.request( + { + method: 'eth_newBlockFilter', + }, + requestOptions, + ) return { id, request: getRequest(id), type: 'block' } } diff --git a/src/actions/public/createContractEventFilter.ts b/src/actions/public/createContractEventFilter.ts index 3746e293a6..76b7c59410 100644 --- a/src/actions/public/createContractEventFilter.ts +++ b/src/actions/public/createContractEventFilter.ts @@ -9,6 +9,7 @@ import type { ContractEventName, MaybeExtractEventArgsFromAbi, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { Hex } from '../../types/misc.js' import { @@ -43,6 +44,8 @@ export type CreateContractEventFilterParameters< */ strict?: strict | boolean | undefined toBlock?: toBlock | BlockNumber | BlockTag | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & (undefined extends eventName ? { args?: undefined @@ -125,8 +128,16 @@ export async function createContractEventFilter< toBlock > > { - const { address, abi, args, eventName, fromBlock, strict, toBlock } = - parameters as CreateContractEventFilterParameters + const { + address, + abi, + args, + eventName, + fromBlock, + strict, + toBlock, + requestOptions, + } = parameters as CreateContractEventFilterParameters const getRequest = createFilterRequestScope(client, { method: 'eth_newFilter', @@ -139,18 +150,21 @@ export async function createContractEventFilter< eventName, } as unknown as EncodeEventTopicsParameters) : undefined - const id: Hex = await client.request({ - method: 'eth_newFilter', - params: [ - { - address, - fromBlock: - typeof fromBlock === 'bigint' ? numberToHex(fromBlock) : fromBlock, - toBlock: typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, - topics, - }, - ], - }) + const id: Hex = await client.request( + { + method: 'eth_newFilter', + params: [ + { + address, + fromBlock: + typeof fromBlock === 'bigint' ? numberToHex(fromBlock) : fromBlock, + toBlock: typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, + topics, + }, + ], + }, + requestOptions, + ) return { abi, diff --git a/src/actions/public/createEventFilter.ts b/src/actions/public/createEventFilter.ts index d2fb1e44fa..1e3388b0b0 100644 --- a/src/actions/public/createEventFilter.ts +++ b/src/actions/public/createEventFilter.ts @@ -9,6 +9,7 @@ import type { MaybeAbiEventName, MaybeExtractEventArgsFromAbi, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { Hex, LogTopic } from '../../types/misc.js' import type { Prettify } from '../../types/utils.js' @@ -42,6 +43,8 @@ export type CreateEventFilterParameters< address?: Address | Address[] | undefined fromBlock?: fromBlock | BlockNumber | BlockTag | undefined toBlock?: toBlock | BlockNumber | BlockTag | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & (MaybeExtractEventArgsFromAbi< abiEvents, _eventName @@ -160,6 +163,7 @@ export async function createEventFilter< event, events: events_, fromBlock, + requestOptions, strict, toBlock, }: CreateEventFilterParameters< @@ -202,18 +206,21 @@ export async function createEventFilter< if (event) topics = topics[0] as LogTopic[] } - const id: Hex = await client.request({ - method: 'eth_newFilter', - params: [ - { - address, - fromBlock: - typeof fromBlock === 'bigint' ? numberToHex(fromBlock) : fromBlock, - toBlock: typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, - ...(topics.length ? { topics } : {}), - }, - ], - }) + const id: Hex = await client.request( + { + method: 'eth_newFilter', + params: [ + { + address, + fromBlock: + typeof fromBlock === 'bigint' ? numberToHex(fromBlock) : fromBlock, + toBlock: typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, + ...(topics.length ? { topics } : {}), + }, + ], + }, + requestOptions, + ) return { abi: events, diff --git a/src/actions/public/createPendingTransactionFilter.ts b/src/actions/public/createPendingTransactionFilter.ts index 049f850748..fa9fe1e782 100644 --- a/src/actions/public/createPendingTransactionFilter.ts +++ b/src/actions/public/createPendingTransactionFilter.ts @@ -2,10 +2,16 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { createFilterRequestScope } from '../../utils/filters/createFilterRequestScope.js' +export type CreatePendingTransactionFilterParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type CreatePendingTransactionFilterReturnType = Filter<'transaction'> export type CreatePendingTransactionFilterErrorType = @@ -19,6 +25,7 @@ export type CreatePendingTransactionFilterErrorType = * - JSON-RPC Methods: [`eth_newPendingTransactionFilter`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newpendingtransactionfilter) * * @param client - Client to use + * @param parameters - {@link CreatePendingTransactionFilterParameters} * @returns [`Filter`](https://viem.sh/docs/glossary/types#filter). {@link CreateBlockFilterReturnType} * * @example @@ -38,12 +45,16 @@ export async function createPendingTransactionFilter< chain extends Chain | undefined, >( client: Client, + { requestOptions }: CreatePendingTransactionFilterParameters = {}, ): Promise { const getRequest = createFilterRequestScope(client, { method: 'eth_newPendingTransactionFilter', }) - const id = await client.request({ - method: 'eth_newPendingTransactionFilter', - }) + const id = await client.request( + { + method: 'eth_newPendingTransactionFilter', + }, + requestOptions, + ) return { id, request: getRequest(id), type: 'transaction' } } diff --git a/src/actions/public/estimateContractGas.ts b/src/actions/public/estimateContractGas.ts index 37f43aa2a7..7db545a30f 100644 --- a/src/actions/public/estimateContractGas.ts +++ b/src/actions/public/estimateContractGas.ts @@ -15,6 +15,7 @@ import type { ContractFunctionParameters, GetValue, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { UnionOmit } from '../../types/utils.js' import { @@ -61,6 +62,8 @@ export type EstimateContractGasParameters< > & { /** Data to append to the end of the calldata. Useful for adding a ["domain" tag](https://opensea.notion.site/opensea/Seaport-Order-Attributions-ec2d69bf455041a5baa490941aad307f). */ dataSuffix?: Hex | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type EstimateContractGasReturnType = bigint diff --git a/src/actions/public/estimateFeesPerGas.ts b/src/actions/public/estimateFeesPerGas.ts index 2ed70749c2..588af09444 100644 --- a/src/actions/public/estimateFeesPerGas.ts +++ b/src/actions/public/estimateFeesPerGas.ts @@ -15,6 +15,7 @@ import type { ChainFeesFnParameters, GetChainParameter, } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { FeeValuesEIP1559, FeeValuesLegacy, @@ -43,6 +44,8 @@ export type EstimateFeesPerGasParameters< * @default 'eip1559' */ type?: type | FeeValuesType | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & GetChainParameter export type EstimateFeesPerGasReturnType< @@ -107,6 +110,7 @@ export async function internal_estimateFeesPerGas< block: block_, chain = client.chain, request, + requestOptions, type = 'eip1559', } = args || {} @@ -129,7 +133,7 @@ export async function internal_estimateFeesPerGas< const block = block_ ? block_ - : await getAction(client, getBlock, 'getBlock')({}) + : await getAction(client, getBlock, 'getBlock')({ requestOptions }) if (typeof chain?.fees?.estimateFeesPerGas === 'function') { const fees = (await chain.fees.estimateFeesPerGas({ @@ -156,6 +160,7 @@ export async function internal_estimateFeesPerGas< block: block as Block, chain, request, + requestOptions, }, ) @@ -171,7 +176,9 @@ export async function internal_estimateFeesPerGas< const gasPrice = request?.gasPrice ?? - multiply(await getAction(client, getGasPrice, 'getGasPrice')({})) + multiply( + await getAction(client, getGasPrice, 'getGasPrice')({ requestOptions }), + ) return { gasPrice, } as EstimateFeesPerGasReturnType diff --git a/src/actions/public/estimateGas.ts b/src/actions/public/estimateGas.ts index 000fb370a4..61ac749f4d 100644 --- a/src/actions/public/estimateGas.ts +++ b/src/actions/public/estimateGas.ts @@ -9,6 +9,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import { BaseError } from '../../errors/base.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { StateOverride } from '../../types/stateOverride.js' import type { TransactionRequest } from '../../types/transaction.js' import type { UnionOmit } from '../../types/utils.js' @@ -50,6 +51,7 @@ export type EstimateGasParameters< | boolean | readonly PrepareTransactionRequestParameterType[] | undefined + requestOptions?: EIP1193RequestOptions | undefined stateOverride?: StateOverride | undefined } & ( | { @@ -156,6 +158,7 @@ export async function estimateGas< maxFeePerGas, maxPriorityFeePerGas, nonce, + requestOptions, value, stateOverride, ...rest @@ -206,18 +209,21 @@ export async function estimateGas< ) return BigInt( - await client.request({ - method: 'eth_estimateGas', - params: rpcStateOverride - ? [ - request, - block ?? client.experimental_blockTag ?? 'latest', - rpcStateOverride, - ] - : block - ? [request, block] - : [request], - }), + await client.request( + { + method: 'eth_estimateGas', + params: rpcStateOverride + ? [ + request, + block ?? client.experimental_blockTag ?? 'latest', + rpcStateOverride, + ] + : block + ? [request, block] + : [request], + }, + requestOptions, + ), ) } catch (err) { throw getEstimateGasError(err as BaseError, { diff --git a/src/actions/public/estimateMaxPriorityFeePerGas.ts b/src/actions/public/estimateMaxPriorityFeePerGas.ts index 035ca64178..7cda061b8c 100644 --- a/src/actions/public/estimateMaxPriorityFeePerGas.ts +++ b/src/actions/public/estimateMaxPriorityFeePerGas.ts @@ -12,6 +12,7 @@ import type { ChainFeesFnParameters, GetChainParameter, } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type HexToBigIntErrorType, @@ -25,7 +26,10 @@ import { type GetGasPriceErrorType, getGasPrice } from './getGasPrice.js' export type EstimateMaxPriorityFeePerGasParameters< chain extends Chain | undefined = Chain | undefined, chainOverride extends Chain | undefined = Chain | undefined, -> = GetChainParameter +> = GetChainParameter & { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} export type EstimateMaxPriorityFeePerGasReturnType = bigint @@ -88,7 +92,12 @@ export async function internal_estimateMaxPriorityFeePerGas< | undefined }, ): Promise { - const { block: block_, chain = client.chain, request } = args || {} + const { + block: block_, + chain = client.chain, + request, + requestOptions, + } = args || {} try { const maxPriorityFeePerGas = @@ -96,7 +105,8 @@ export async function internal_estimateMaxPriorityFeePerGas< if (typeof maxPriorityFeePerGas === 'function') { const block = - block_ || (await getAction(client, getBlock, 'getBlock')({})) + block_ || + (await getAction(client, getBlock, 'getBlock')({ requestOptions })) const maxPriorityFeePerGas_ = await maxPriorityFeePerGas({ block, client, @@ -108,9 +118,12 @@ export async function internal_estimateMaxPriorityFeePerGas< if (typeof maxPriorityFeePerGas !== 'undefined') return maxPriorityFeePerGas - const maxPriorityFeePerGasHex = await client.request({ - method: 'eth_maxPriorityFeePerGas', - }) + const maxPriorityFeePerGasHex = await client.request( + { + method: 'eth_maxPriorityFeePerGas', + }, + requestOptions, + ) return hexToBigInt(maxPriorityFeePerGasHex) } catch { // If the RPC Provider does not support `eth_maxPriorityFeePerGas` @@ -119,8 +132,8 @@ export async function internal_estimateMaxPriorityFeePerGas< const [block, gasPrice] = await Promise.all([ block_ ? Promise.resolve(block_) - : getAction(client, getBlock, 'getBlock')({}), - getAction(client, getGasPrice, 'getGasPrice')({}), + : getAction(client, getBlock, 'getBlock')({ requestOptions }), + getAction(client, getGasPrice, 'getGasPrice')({ requestOptions }), ]) if (typeof block.baseFeePerGas !== 'bigint') diff --git a/src/actions/public/getBalance.ts b/src/actions/public/getBalance.ts index ca7e845c0d..ac4109444d 100644 --- a/src/actions/public/getBalance.ts +++ b/src/actions/public/getBalance.ts @@ -5,6 +5,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type NumberToHexErrorType, @@ -14,6 +15,8 @@ import { export type GetBalanceParameters = { /** The address of the account. */ address: Address + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** The balance of the account at a block number. */ @@ -75,14 +78,18 @@ export async function getBalance( address, blockNumber, blockTag = client.experimental_blockTag ?? 'latest', + requestOptions, }: GetBalanceParameters, ): Promise { const blockNumberHex = typeof blockNumber === 'bigint' ? numberToHex(blockNumber) : undefined - const balance = await client.request({ - method: 'eth_getBalance', - params: [address, blockNumberHex || blockTag], - }) + const balance = await client.request( + { + method: 'eth_getBalance', + params: [address, blockNumberHex || blockTag], + }, + requestOptions, + ) return BigInt(balance) } diff --git a/src/actions/public/getBlobBaseFee.ts b/src/actions/public/getBlobBaseFee.ts index 223f7c57c8..71a92f7f26 100644 --- a/src/actions/public/getBlobBaseFee.ts +++ b/src/actions/public/getBlobBaseFee.ts @@ -3,8 +3,14 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' +export type GetBlobBaseFeeParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type GetBlobBaseFeeReturnType = bigint export type GetBlobBaseFeeErrorType = RequestErrorType | ErrorType @@ -34,9 +40,13 @@ export async function getBlobBaseFee< account extends Account | undefined, >( client: Client, + { requestOptions }: GetBlobBaseFeeParameters = {}, ): Promise { - const baseFee = await client.request({ - method: 'eth_blobBaseFee', - }) + const baseFee = await client.request( + { + method: 'eth_blobBaseFee', + }, + requestOptions, + ) return BigInt(baseFee) } diff --git a/src/actions/public/getBlock.ts b/src/actions/public/getBlock.ts index 890407070f..d17a1d40ca 100644 --- a/src/actions/public/getBlock.ts +++ b/src/actions/public/getBlock.ts @@ -8,6 +8,7 @@ import { import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { RpcBlock } from '../../types/rpc.js' import type { Prettify } from '../../types/utils.js' @@ -27,6 +28,8 @@ export type GetBlockParameters< > = { /** Whether or not to include transaction data in the response. */ includeTransactions?: includeTransactions | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** Hash of the block. */ @@ -99,6 +102,7 @@ export async function getBlock< blockNumber, blockTag = client.experimental_blockTag ?? 'latest', includeTransactions: includeTransactions_, + requestOptions, }: GetBlockParameters = {}, ): Promise> { const includeTransactions = includeTransactions_ ?? false @@ -113,7 +117,7 @@ export async function getBlock< method: 'eth_getBlockByHash', params: [blockHash, includeTransactions], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) } else { block = await client.request( @@ -121,7 +125,7 @@ export async function getBlock< method: 'eth_getBlockByNumber', params: [blockNumberHex || blockTag, includeTransactions], }, - { dedupe: Boolean(blockNumberHex) }, + { dedupe: Boolean(blockNumberHex), ...requestOptions }, ) } diff --git a/src/actions/public/getBlockNumber.test.ts b/src/actions/public/getBlockNumber.test.ts index a124f0a389..88986f8bd9 100644 --- a/src/actions/public/getBlockNumber.test.ts +++ b/src/actions/public/getBlockNumber.test.ts @@ -46,3 +46,55 @@ test('behavior: caches blockNumber within cacheTime', async () => { vi.useRealTimers() }) + +test('requestOptions: cancellation with AbortController', async () => { + const controller = new AbortController() + + const promise = getBlockNumber(client, { + requestOptions: { + signal: controller.signal, + }, + }) + + // Cancel the request immediately + controller.abort() + + await expect(promise).rejects.toThrow() +}) + +test('requestOptions: successful request with signal', async () => { + const controller = new AbortController() + + const blockNumber = await getBlockNumber(client, { + requestOptions: { + signal: controller.signal, + }, + }) + + expect(blockNumber).toBeDefined() + expect(typeof blockNumber).toBe('bigint') +}) + +test('requestOptions: already aborted signal', async () => { + const controller = new AbortController() + controller.abort() + + const promise = getBlockNumber(client, { + requestOptions: { + signal: controller.signal, + }, + }) + + await expect(promise).rejects.toThrow() +}) + +test('requestOptions: custom retry settings', async () => { + const blockNumber = await getBlockNumber(client, { + requestOptions: { + retryCount: 0, + retryDelay: 0, + }, + }) + + expect(blockNumber).toBeDefined() +}) diff --git a/src/actions/public/getBlockNumber.ts b/src/actions/public/getBlockNumber.ts index 3ef84bc8a8..361835eea9 100644 --- a/src/actions/public/getBlockNumber.ts +++ b/src/actions/public/getBlockNumber.ts @@ -2,6 +2,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type GetCacheErrorType, @@ -12,6 +13,8 @@ import { export type GetBlockNumberParameters = { /** Time (in ms) that cached block number will remain in memory. */ cacheTime?: number | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type GetBlockNumberReturnType = bigint @@ -53,13 +56,19 @@ export function getBlockNumberCache(id: string) { */ export async function getBlockNumber( client: Client, - { cacheTime = client.cacheTime }: GetBlockNumberParameters = {}, + { + cacheTime = client.cacheTime, + requestOptions, + }: GetBlockNumberParameters = {}, ): Promise { const blockNumberHex = await withCache( () => - client.request({ - method: 'eth_blockNumber', - }), + client.request( + { + method: 'eth_blockNumber', + }, + requestOptions, + ), { cacheKey: cacheKey(client.uid), cacheTime }, ) return BigInt(blockNumberHex) diff --git a/src/actions/public/getBlockTransactionCount.ts b/src/actions/public/getBlockTransactionCount.ts index 79db01322d..bbc17fba07 100644 --- a/src/actions/public/getBlockTransactionCount.ts +++ b/src/actions/public/getBlockTransactionCount.ts @@ -3,6 +3,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { Quantity } from '../../types/rpc.js' import type { RequestErrorType } from '../../utils/buildRequest.js' @@ -15,7 +16,10 @@ import { numberToHex, } from '../../utils/encoding/toHex.js' -export type GetBlockTransactionCountParameters = +export type GetBlockTransactionCountParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} & ( | { /** Hash of the block. */ blockHash?: Hash | undefined @@ -34,6 +38,7 @@ export type GetBlockTransactionCountParameters = /** The block tag. Defaults to 'latest'. */ blockTag?: BlockTag | undefined } +) export type GetBlockTransactionCountReturnType = number @@ -72,6 +77,7 @@ export async function getBlockTransactionCount( blockHash, blockNumber, blockTag = 'latest', + requestOptions, }: GetBlockTransactionCountParameters = {}, ): Promise { const blockNumberHex = @@ -84,7 +90,7 @@ export async function getBlockTransactionCount( method: 'eth_getBlockTransactionCountByHash', params: [blockHash], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) } else { count = await client.request( @@ -92,7 +98,7 @@ export async function getBlockTransactionCount( method: 'eth_getBlockTransactionCountByNumber', params: [blockNumberHex || blockTag], }, - { dedupe: Boolean(blockNumberHex) }, + { dedupe: Boolean(blockNumberHex), ...requestOptions }, ) } diff --git a/src/actions/public/getChainId.ts b/src/actions/public/getChainId.ts index 532d7c7b26..c7011d2790 100644 --- a/src/actions/public/getChainId.ts +++ b/src/actions/public/getChainId.ts @@ -3,12 +3,18 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type HexToNumberErrorType, hexToNumber, } from '../../utils/encoding/fromHex.js' +export type GetChainIdParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type GetChainIdReturnType = number export type GetChainIdErrorType = @@ -40,12 +46,15 @@ export type GetChainIdErrorType = export async function getChainId< chain extends Chain | undefined, account extends Account | undefined, ->(client: Client): Promise { +>( + client: Client, + { requestOptions }: GetChainIdParameters = {}, +): Promise { const chainIdHex = await client.request( { method: 'eth_chainId', }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) return hexToNumber(chainIdHex) } diff --git a/src/actions/public/getCode.ts b/src/actions/public/getCode.ts index 8c85a24e82..67c4ef5db8 100644 --- a/src/actions/public/getCode.ts +++ b/src/actions/public/getCode.ts @@ -5,6 +5,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { @@ -14,6 +15,8 @@ import { export type GetCodeParameters = { address: Address + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { blockNumber?: undefined @@ -57,7 +60,12 @@ export type GetCodeErrorType = */ export async function getCode( client: Client, - { address, blockNumber, blockTag = 'latest' }: GetCodeParameters, + { + address, + blockNumber, + blockTag = 'latest', + requestOptions, + }: GetCodeParameters, ): Promise { const blockNumberHex = blockNumber !== undefined ? numberToHex(blockNumber) : undefined @@ -66,7 +74,7 @@ export async function getCode( method: 'eth_getCode', params: [address, blockNumberHex || blockTag], }, - { dedupe: Boolean(blockNumberHex) }, + { dedupe: Boolean(blockNumberHex), ...requestOptions }, ) if (hex === '0x') return undefined return hex diff --git a/src/actions/public/getContractEvents.ts b/src/actions/public/getContractEvents.ts index bc1019a72c..0290cae667 100644 --- a/src/actions/public/getContractEvents.ts +++ b/src/actions/public/getContractEvents.ts @@ -9,6 +9,7 @@ import type { ContractEventArgs, ContractEventName, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Log } from '../../types/log.js' import type { Hash } from '../../types/misc.js' import { @@ -51,6 +52,8 @@ export type GetContractEventsParameters< * @default false */ strict?: strict | boolean | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** Block number or tag after which to include logs */ @@ -140,6 +143,7 @@ export async function getContractEvents< fromBlock, toBlock, strict, + requestOptions, } = parameters const event = eventName ? getAbiItem({ abi, name: eventName } as GetAbiItemParameters) @@ -160,6 +164,7 @@ export async function getContractEvents< fromBlock, toBlock, strict, + requestOptions, } as {} as GetLogsParameters) as unknown as GetContractEventsReturnType< abi, eventName, diff --git a/src/actions/public/getEip712Domain.ts b/src/actions/public/getEip712Domain.ts index 3cd01bf908..f5fcedb5a2 100644 --- a/src/actions/public/getEip712Domain.ts +++ b/src/actions/public/getEip712Domain.ts @@ -17,7 +17,7 @@ import { export type GetEip712DomainParameters = { address: Address -} & Pick +} & Pick export type GetEip712DomainReturnType = { domain: RequiredBy< @@ -69,7 +69,7 @@ export async function getEip712Domain( client: Client, parameters: GetEip712DomainParameters, ): Promise { - const { address, factory, factoryData } = parameters + const { address, factory, factoryData, requestOptions } = parameters try { const [ @@ -90,6 +90,7 @@ export async function getEip712Domain( functionName: 'eip712Domain', factory, factoryData, + requestOptions, }) return { diff --git a/src/actions/public/getFeeHistory.ts b/src/actions/public/getFeeHistory.ts index 9e5a83fce6..7b5f257f84 100644 --- a/src/actions/public/getFeeHistory.ts +++ b/src/actions/public/getFeeHistory.ts @@ -2,6 +2,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { FeeHistory } from '../../types/fee.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { @@ -22,6 +23,8 @@ export type GetFeeHistoryParameters = { * A monotonically increasing list of percentile values to sample from each block's effective priority fees per gas in ascending order, weighted by gas used. */ rewardPercentiles: number[] + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { blockNumber?: undefined @@ -75,6 +78,7 @@ export async function getFeeHistory( blockNumber, blockTag = 'latest', rewardPercentiles, + requestOptions, }: GetFeeHistoryParameters, ): Promise { const blockNumberHex = @@ -88,7 +92,7 @@ export async function getFeeHistory( rewardPercentiles, ], }, - { dedupe: Boolean(blockNumberHex) }, + { dedupe: Boolean(blockNumberHex), ...requestOptions }, ) return formatFeeHistory(feeHistory) } diff --git a/src/actions/public/getFilterChanges.ts b/src/actions/public/getFilterChanges.ts index e9fb6ad9d0..215cdc4e62 100644 --- a/src/actions/public/getFilterChanges.ts +++ b/src/actions/public/getFilterChanges.ts @@ -6,6 +6,7 @@ import type { ErrorType } from '../../errors/utils.js' import type { RpcLog } from '../../index.js' import type { BlockNumber, BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter, FilterType } from '../../types/filter.js' import type { Log } from '../../types/log.js' import type { Hash } from '../../types/misc.js' @@ -26,6 +27,8 @@ export type GetFilterChangesParameters< toBlock extends BlockNumber | BlockTag | undefined = undefined, > = { filter: Filter + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type GetFilterChangesReturnType< @@ -148,6 +151,7 @@ export async function getFilterChanges< _client: Client, { filter, + requestOptions, }: GetFilterChangesParameters< filterType, abi, @@ -168,10 +172,13 @@ export async function getFilterChanges< > { const strict = 'strict' in filter && filter.strict - const logs = await filter.request({ - method: 'eth_getFilterChanges', - params: [filter.id], - }) + const logs = await filter.request( + { + method: 'eth_getFilterChanges', + params: [filter.id], + }, + requestOptions, + ) if (typeof logs[0] === 'string') return logs as GetFilterChangesReturnType< diff --git a/src/actions/public/getFilterLogs.ts b/src/actions/public/getFilterLogs.ts index 207fd8c430..1b3898230b 100644 --- a/src/actions/public/getFilterLogs.ts +++ b/src/actions/public/getFilterLogs.ts @@ -6,6 +6,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockNumber, BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { Log } from '../../types/log.js' import type { DecodeEventLogErrorType } from '../../utils/abi/decodeEventLog.js' @@ -24,6 +25,8 @@ export type GetFilterLogsParameters< toBlock extends BlockNumber | BlockTag | undefined = undefined, > = { filter: Filter<'event', abi, eventName, any, strict, fromBlock, toBlock> + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type GetFilterLogsReturnType< abi extends Abi | readonly unknown[] | undefined = undefined, @@ -85,16 +88,20 @@ export async function getFilterLogs< _client: Client, { filter, + requestOptions, }: GetFilterLogsParameters, ): Promise< GetFilterLogsReturnType > { const strict = filter.strict ?? false - const logs = await filter.request({ - method: 'eth_getFilterLogs', - params: [filter.id], - }) + const logs = await filter.request( + { + method: 'eth_getFilterLogs', + params: [filter.id], + }, + requestOptions, + ) const formattedLogs = logs.map((log) => formatLog(log)) if (!filter.abi) diff --git a/src/actions/public/getGasPrice.ts b/src/actions/public/getGasPrice.ts index 1320f2bad5..836f598c71 100644 --- a/src/actions/public/getGasPrice.ts +++ b/src/actions/public/getGasPrice.ts @@ -3,8 +3,14 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' +export type GetGasPriceParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type GetGasPriceReturnType = bigint export type GetGasPriceErrorType = RequestErrorType | ErrorType @@ -32,9 +38,15 @@ export type GetGasPriceErrorType = RequestErrorType | ErrorType export async function getGasPrice< chain extends Chain | undefined, account extends Account | undefined, ->(client: Client): Promise { - const gasPrice = await client.request({ - method: 'eth_gasPrice', - }) +>( + client: Client, + { requestOptions }: GetGasPriceParameters = {}, +): Promise { + const gasPrice = await client.request( + { + method: 'eth_gasPrice', + }, + requestOptions, + ) return BigInt(gasPrice) } diff --git a/src/actions/public/getLogs.ts b/src/actions/public/getLogs.ts index 269c9bc560..02a9c950b0 100644 --- a/src/actions/public/getLogs.ts +++ b/src/actions/public/getLogs.ts @@ -9,6 +9,7 @@ import type { MaybeAbiEventName, MaybeExtractEventArgsFromAbi, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Log } from '../../types/log.js' import type { Hash, LogTopic } from '../../types/misc.js' import type { RpcLog } from '../../types/rpc.js' @@ -43,6 +44,8 @@ export type GetLogsParameters< > = { /** Address or list of addresses from which logs originated */ address?: Address | Address[] | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { event: abiEvent @@ -153,6 +156,7 @@ export async function getLogs< event, events: events_, args, + requestOptions, strict: strict_, }: GetLogsParameters = {}, ): Promise> { @@ -175,23 +179,32 @@ export async function getLogs< let logs: RpcLog[] if (blockHash) { - logs = await client.request({ - method: 'eth_getLogs', - params: [{ address, topics, blockHash }], - }) + logs = await client.request( + { + method: 'eth_getLogs', + params: [{ address, topics, blockHash }], + }, + requestOptions, + ) } else { - logs = await client.request({ - method: 'eth_getLogs', - params: [ - { - address, - topics, - fromBlock: - typeof fromBlock === 'bigint' ? numberToHex(fromBlock) : fromBlock, - toBlock: typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, - }, - ], - }) + logs = await client.request( + { + method: 'eth_getLogs', + params: [ + { + address, + topics, + fromBlock: + typeof fromBlock === 'bigint' + ? numberToHex(fromBlock) + : fromBlock, + toBlock: + typeof toBlock === 'bigint' ? numberToHex(toBlock) : toBlock, + }, + ], + }, + requestOptions, + ) } const formattedLogs = logs.map((log) => formatLog(log)) diff --git a/src/actions/public/getProof.ts b/src/actions/public/getProof.ts index 83a3b7169e..622dce17ce 100644 --- a/src/actions/public/getProof.ts +++ b/src/actions/public/getProof.ts @@ -4,6 +4,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { Proof } from '../../types/proof.js' import type { RequestErrorType } from '../../utils/buildRequest.js' @@ -21,6 +22,8 @@ export type GetProofParameters = { address: Address /** Array of storage-keys that should be proofed and included. */ storageKeys: Hash[] + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** The block number. */ @@ -77,6 +80,7 @@ export async function getProof( blockNumber, blockTag: blockTag_, storageKeys, + requestOptions, }: GetProofParameters, ): Promise { const blockTag = blockTag_ ?? 'latest' @@ -84,10 +88,13 @@ export async function getProof( const blockNumberHex = blockNumber !== undefined ? numberToHex(blockNumber) : undefined - const proof = await client.request({ - method: 'eth_getProof', - params: [address, storageKeys, blockNumberHex || blockTag], - }) + const proof = await client.request( + { + method: 'eth_getProof', + params: [address, storageKeys, blockNumberHex || blockTag], + }, + requestOptions, + ) return formatProof(proof) } diff --git a/src/actions/public/getStorageAt.ts b/src/actions/public/getStorageAt.ts index b4e350c1fa..cf654b8a3b 100644 --- a/src/actions/public/getStorageAt.ts +++ b/src/actions/public/getStorageAt.ts @@ -5,6 +5,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { @@ -15,6 +16,8 @@ import { export type GetStorageAtParameters = { address: Address slot: Hex + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { blockNumber?: undefined @@ -59,13 +62,22 @@ export type GetStorageAtErrorType = */ export async function getStorageAt( client: Client, - { address, blockNumber, blockTag = 'latest', slot }: GetStorageAtParameters, + { + address, + blockNumber, + blockTag = 'latest', + slot, + requestOptions, + }: GetStorageAtParameters, ): Promise { const blockNumberHex = blockNumber !== undefined ? numberToHex(blockNumber) : undefined - const data = await client.request({ - method: 'eth_getStorageAt', - params: [address, slot, blockNumberHex || blockTag], - }) + const data = await client.request( + { + method: 'eth_getStorageAt', + params: [address, slot, blockNumberHex || blockTag], + }, + requestOptions, + ) return data } diff --git a/src/actions/public/getTransaction.ts b/src/actions/public/getTransaction.ts index fe6cf8b79f..4f1dc00bb6 100644 --- a/src/actions/public/getTransaction.ts +++ b/src/actions/public/getTransaction.ts @@ -8,6 +8,7 @@ import { import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { RpcTransaction } from '../../types/rpc.js' import type { OneOf, Prettify } from '../../types/utils.js' @@ -56,7 +57,10 @@ export type GetTransactionParameters = /** The nonce of the transaction on the sender. */ nonce: number } - > + > & { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined + } export type GetTransactionReturnType< chain extends Chain | undefined = undefined, @@ -106,6 +110,7 @@ export async function getTransaction< index, sender, nonce, + requestOptions, }: GetTransactionParameters, ): Promise> { const blockTag = blockTag_ || 'latest' @@ -120,7 +125,7 @@ export async function getTransaction< method: 'eth_getTransactionByHash', params: [hash], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) } else if (blockHash) { transaction = await client.request( @@ -128,7 +133,7 @@ export async function getTransaction< method: 'eth_getTransactionByBlockHashAndIndex', params: [blockHash, numberToHex(index)], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) } else if ((blockNumberHex || blockTag) && typeof index === 'number') { transaction = await client.request( @@ -136,7 +141,7 @@ export async function getTransaction< method: 'eth_getTransactionByBlockNumberAndIndex', params: [blockNumberHex || blockTag, numberToHex(index)], }, - { dedupe: Boolean(blockNumberHex) }, + { dedupe: Boolean(blockNumberHex), ...requestOptions }, ) } else if (sender && typeof nonce === 'number') { transaction = await client.request( diff --git a/src/actions/public/getTransactionConfirmations.ts b/src/actions/public/getTransactionConfirmations.ts index 6d2fe2a7d5..b0cb8d7a8f 100644 --- a/src/actions/public/getTransactionConfirmations.ts +++ b/src/actions/public/getTransactionConfirmations.ts @@ -2,6 +2,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { FormattedTransactionReceipt } from '../../utils/formatters/transactionReceipt.js' import { getAction } from '../../utils/getAction.js' @@ -17,7 +18,10 @@ import { export type GetTransactionConfirmationsParameters< chain extends Chain | undefined = Chain, -> = +> = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} & ( | { /** The transaction hash. */ hash: Hash @@ -28,6 +32,7 @@ export type GetTransactionConfirmationsParameters< /** The transaction receipt. */ transactionReceipt: FormattedTransactionReceipt } +) export type GetTransactionConfirmationsReturnType = bigint @@ -64,12 +69,20 @@ export async function getTransactionConfirmations< chain extends Chain | undefined, >( client: Client, - { hash, transactionReceipt }: GetTransactionConfirmationsParameters, + { + hash, + transactionReceipt, + requestOptions, + }: GetTransactionConfirmationsParameters, ): Promise { const [blockNumber, transaction] = await Promise.all([ - getAction(client, getBlockNumber, 'getBlockNumber')({}), + getAction(client, getBlockNumber, 'getBlockNumber')({ requestOptions }), hash - ? getAction(client, getTransaction, 'getTransaction')({ hash }) + ? getAction( + client, + getTransaction, + 'getTransaction', + )({ hash, requestOptions }) : undefined, ]) const transactionBlockNumber = diff --git a/src/actions/public/getTransactionCount.ts b/src/actions/public/getTransactionCount.ts index f100eb5cf6..9ebff76e75 100644 --- a/src/actions/public/getTransactionCount.ts +++ b/src/actions/public/getTransactionCount.ts @@ -6,6 +6,7 @@ import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type HexToNumberErrorType, @@ -19,6 +20,8 @@ import { export type GetTransactionCountParameters = { /** The account address. */ address: Address + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } & ( | { /** The block number. */ @@ -67,7 +70,12 @@ export async function getTransactionCount< account extends Account | undefined, >( client: Client, - { address, blockTag = 'latest', blockNumber }: GetTransactionCountParameters, + { + address, + blockTag = 'latest', + blockNumber, + requestOptions, + }: GetTransactionCountParameters, ): Promise { const count = await client.request( { @@ -79,6 +87,7 @@ export async function getTransactionCount< }, { dedupe: Boolean(blockNumber), + ...requestOptions, }, ) return hexToNumber(count) diff --git a/src/actions/public/getTransactionReceipt.ts b/src/actions/public/getTransactionReceipt.ts index 7c87f3503b..daa877a5c1 100644 --- a/src/actions/public/getTransactionReceipt.ts +++ b/src/actions/public/getTransactionReceipt.ts @@ -6,6 +6,7 @@ import { } from '../../errors/transaction.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { @@ -16,6 +17,8 @@ import { export type GetTransactionReceiptParameters = { /** The hash of the transaction. */ hash: Hash + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type GetTransactionReceiptReturnType< @@ -53,14 +56,14 @@ export type GetTransactionReceiptErrorType = */ export async function getTransactionReceipt( client: Client, - { hash }: GetTransactionReceiptParameters, + { hash, requestOptions }: GetTransactionReceiptParameters, ) { const receipt = await client.request( { method: 'eth_getTransactionReceipt', params: [hash], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) if (!receipt) throw new TransactionReceiptNotFoundError({ hash }) diff --git a/src/actions/public/multicall.ts b/src/actions/public/multicall.ts index abd853285a..17efd9eb5b 100644 --- a/src/actions/public/multicall.ts +++ b/src/actions/public/multicall.ts @@ -49,6 +49,7 @@ export type MulticallParameters< | 'blockOverrides' | 'blockTag' | 'stateOverride' + | 'requestOptions' > & { /** The account to use for the multicall. */ account?: Address | undefined @@ -142,6 +143,7 @@ export async function multicall< blockOverrides, blockTag, stateOverride, + requestOptions, } = parameters const contracts = parameters.contracts as ContractFunctionParameters[] @@ -242,6 +244,7 @@ export async function multicall< blockTag, functionName: 'aggregate3', stateOverride, + requestOptions, }), ), ) diff --git a/src/actions/public/readContract.ts b/src/actions/public/readContract.ts index 352400848f..a1222fac03 100644 --- a/src/actions/public/readContract.ts +++ b/src/actions/public/readContract.ts @@ -50,6 +50,7 @@ export type ReadContractParameters< | 'factory' | 'factoryData' | 'stateOverride' + | 'requestOptions' > > & ContractFunctionParameters diff --git a/src/actions/public/simulateBlocks.ts b/src/actions/public/simulateBlocks.ts index 1f512c6e07..49036a6041 100644 --- a/src/actions/public/simulateBlocks.ts +++ b/src/actions/public/simulateBlocks.ts @@ -16,6 +16,7 @@ import type { Account } from '../../types/account.js' import type { Block, BlockTag } from '../../types/block.js' import type { Call, Calls } from '../../types/calls.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Log } from '../../types/log.js' import type { Hex } from '../../types/misc.js' import type { MulticallResults } from '../../types/multicall.js' @@ -30,6 +31,7 @@ import { type EncodeFunctionDataErrorType, encodeFunctionData, } from '../../utils/abi/encodeFunctionData.js' +import type { RequestErrorType } from '../../utils/buildRequest.js' import { concat } from '../../utils/data/concat.js' import { type NumberToHexErrorType, @@ -82,6 +84,8 @@ export type SimulateBlocksParameters< /** State overrides. */ stateOverrides?: StateOverride | undefined }[] + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined /** Whether to return the full transactions. */ returnFullTransactions?: boolean | undefined /** Whether to trace transfers. */ @@ -132,6 +136,7 @@ export type SimulateBlocksErrorType = | ParseAccountErrorType | SerializeStateOverrideErrorType | NumberToHexErrorType + | RequestErrorType | ErrorType /** @@ -188,6 +193,7 @@ export async function simulateBlocks< blockNumber, blockTag = client.experimental_blockTag ?? 'latest', blocks, + requestOptions, returnFullTransactions, traceTransfers, validation, @@ -229,13 +235,21 @@ export async function simulateBlocks< typeof blockNumber === 'bigint' ? numberToHex(blockNumber) : undefined const block = blockNumberHex || blockTag - const result = await client.request({ - method: 'eth_simulateV1', - params: [ - { blockStateCalls, returnFullTransactions, traceTransfers, validation }, - block, - ], - }) + const result = await client.request( + { + method: 'eth_simulateV1', + params: [ + { + blockStateCalls, + returnFullTransactions, + traceTransfers, + validation, + }, + block, + ], + }, + requestOptions, + ) return result.map((block, i) => ({ ...formatBlock(block), diff --git a/src/actions/public/simulateCalls.ts b/src/actions/public/simulateCalls.ts index c418173fbd..173210b7e8 100644 --- a/src/actions/public/simulateCalls.ts +++ b/src/actions/public/simulateCalls.ts @@ -133,6 +133,7 @@ export async function simulateCalls< blockNumber, blockTag, calls, + requestOptions, stateOverrides, traceAssetChanges, traceTransfers, @@ -171,6 +172,7 @@ export async function simulateCalls< account: account!.address, ...call, data: call.abi ? encodeFunctionData(call) : call.data, + requestOptions, }) return accessList.map(({ address, storageKeys }) => storageKeys.length > 0 ? address : null, @@ -182,6 +184,7 @@ export async function simulateCalls< const blocks = await simulateBlocks(client, { blockNumber, blockTag: blockTag as undefined, + requestOptions, blocks: [ ...(traceAssetChanges ? [ diff --git a/src/actions/public/uninstallFilter.ts b/src/actions/public/uninstallFilter.ts index 2a3e7c709a..adb1d0b39d 100644 --- a/src/actions/public/uninstallFilter.ts +++ b/src/actions/public/uninstallFilter.ts @@ -2,11 +2,14 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Filter } from '../../types/filter.js' import type { RequestErrorType } from '../../utils/buildRequest.js' export type UninstallFilterParameters = { filter: Filter + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type UninstallFilterReturnType = boolean @@ -41,10 +44,13 @@ export async function uninstallFilter< chain extends Chain | undefined, >( _client: Client, - { filter }: UninstallFilterParameters, + { filter, requestOptions }: UninstallFilterParameters, ): Promise { - return filter.request({ - method: 'eth_uninstallFilter', - params: [filter.id], - }) + return filter.request( + { + method: 'eth_uninstallFilter', + params: [filter.id], + }, + requestOptions, + ) } diff --git a/src/actions/public/verifyHash.ts b/src/actions/public/verifyHash.ts index 4d9afe299c..8dce301ba5 100644 --- a/src/actions/public/verifyHash.ts +++ b/src/actions/public/verifyHash.ts @@ -20,6 +20,7 @@ import { import type { InvalidHexBooleanError } from '../../errors/encoding.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { ByteArray, Hex, Signature } from '../../types/misc.js' import type { OneOf } from '../../types/utils.js' import { @@ -75,6 +76,8 @@ export type VerifyHashParameters = Pick< hash: Hex /** Multicall3 address for ERC-8010 verification. */ multicallAddress?: Address | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined /** The signature that was generated by signing the message with the address's private key. */ signature: Hex | ByteArray | Signature /** @deprecated use `erc6492VerifierAddress` instead. */ diff --git a/src/actions/public/waitForTransactionReceipt.test.ts b/src/actions/public/waitForTransactionReceipt.test.ts index 9683712cef..636354813c 100644 --- a/src/actions/public/waitForTransactionReceipt.test.ts +++ b/src/actions/public/waitForTransactionReceipt.test.ts @@ -614,6 +614,54 @@ test('args: timeout', async () => { ).rejects.toThrowError(WaitForTransactionReceiptTimeoutError) }) +test('args: requestOptions.signal (already aborted)', async () => { + setup() + + const hash = await sendTransaction(client, { + account: sourceAccount.address, + to: targetAccount.address, + value: parseEther('1'), + }) + + const controller = new AbortController() + controller.abort() + + await expect(() => + waitForTransactionReceipt(client, { + hash, + requestOptions: { signal: controller.signal }, + }), + ).rejects.toThrow('This operation was aborted') +}) + +test('args: requestOptions.signal (aborted during wait)', async () => { + setup() + + const hash = await sendTransaction(client, { + account: sourceAccount.address, + to: targetAccount.address, + value: parseEther('1'), + }) + + const controller = new AbortController() + + const receiptPromise = waitForTransactionReceipt(client, { + hash, + pollingInterval: 50, + timeout: 10_000, + requestOptions: { signal: controller.signal }, + }) + + await wait(100) + controller.abort() + + const start = Date.now() + await expect(() => receiptPromise).rejects.toThrow() + const elapsed = Date.now() - start + + expect(elapsed).toBeLessThan(2000) +}) + describe('errors', () => { test('throws when transaction replaced and getBlock fails', async () => { setup() diff --git a/src/actions/public/waitForTransactionReceipt.ts b/src/actions/public/waitForTransactionReceipt.ts index 98246a5411..38776a67e3 100644 --- a/src/actions/public/waitForTransactionReceipt.ts +++ b/src/actions/public/waitForTransactionReceipt.ts @@ -9,6 +9,7 @@ import { } from '../../errors/transaction.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { Transaction } from '../../types/transaction.js' import { getAction } from '../../utils/getAction.js' @@ -72,6 +73,8 @@ export type WaitForTransactionReceiptParameters< * @default client.pollingInterval */ pollingInterval?: number | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined /** * Number of times to retry if the transaction or block is not found. * @default 6 (exponential backoff) @@ -147,6 +150,7 @@ export async function waitForTransactionReceipt< confirmations = 1, hash, onReplaced, + requestOptions, retryCount = 6, retryDelay = ({ count }) => ~~(1 << count) * 200, // exponential backoff timeout = 180_000, @@ -188,7 +192,10 @@ export async function waitForTransactionReceipt< client, getTransactionReceipt, 'getTransactionReceipt', - )({ hash }).catch(() => undefined) + )({ hash, requestOptions }).catch((err) => { + if (err?.name === 'AbortError') throw err + return undefined + }) if (receipt && confirmations <= 1) { clearTimeout(timer) @@ -244,13 +251,17 @@ export async function waitForTransactionReceipt< client, getTransaction, 'getTransaction', - )({ hash })) as GetTransactionReturnType + )({ + hash, + requestOptions, + })) as GetTransactionReturnType if (transaction.blockNumber) blockNumber = transaction.blockNumber }, { delay: retryDelay, retryCount, + signal: requestOptions?.signal, }, ) retrying = false @@ -261,7 +272,7 @@ export async function waitForTransactionReceipt< client, getTransactionReceipt, 'getTransactionReceipt', - )({ hash }) + )({ hash, requestOptions }) // Check if we have enough confirmations. If not, continue polling. if ( @@ -300,12 +311,14 @@ export async function waitForTransactionReceipt< )({ blockNumber, includeTransactions: true, + requestOptions, }), { delay: retryDelay, retryCount, shouldRetry: ({ error }) => error instanceof BlockNotFoundError, + signal: requestOptions?.signal, }, ) retrying = false @@ -328,6 +341,7 @@ export async function waitForTransactionReceipt< 'getTransactionReceipt', )({ hash: replacementTransaction.hash, + requestOptions, }) // Check if we have enough confirmations. If not, continue polling. diff --git a/src/actions/wallet/addChain.ts b/src/actions/wallet/addChain.ts index 04c85d0fe7..12ff9da57c 100644 --- a/src/actions/wallet/addChain.ts +++ b/src/actions/wallet/addChain.ts @@ -3,6 +3,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type NumberToHexErrorType, @@ -12,6 +13,8 @@ import { export type AddChainParameters = { /** The chain to add to the wallet. */ chain: Chain + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type AddChainErrorType = @@ -41,7 +44,10 @@ export type AddChainErrorType = export async function addChain< chain extends Chain | undefined, account extends Account | undefined, ->(client: Client, { chain }: AddChainParameters) { +>( + client: Client, + { chain, requestOptions }: AddChainParameters, +) { const { id, name, nativeCurrency, rpcUrls, blockExplorers } = chain await client.request( { @@ -58,6 +64,6 @@ export async function addChain< }, ], }, - { dedupe: true, retryCount: 0 }, + { dedupe: true, retryCount: 0, ...requestOptions }, ) } diff --git a/src/actions/wallet/getAddresses.ts b/src/actions/wallet/getAddresses.ts index 5b7c532f24..634f642854 100644 --- a/src/actions/wallet/getAddresses.ts +++ b/src/actions/wallet/getAddresses.ts @@ -5,12 +5,18 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import { type ChecksumAddressErrorType, checksumAddress, } from '../../utils/address/getAddress.js' import type { RequestErrorType } from '../../utils/buildRequest.js' +export type GetAddressesParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type GetAddressesReturnType = Address[] export type GetAddressesErrorType = @@ -25,6 +31,7 @@ export type GetAddressesErrorType = * - JSON-RPC Methods: [`eth_accounts`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_accounts) * * @param client - Client to use + * @param parameters - {@link GetAddressesParameters} * @returns List of account addresses owned by the wallet or client. {@link GetAddressesReturnType} * * @example @@ -41,11 +48,14 @@ export type GetAddressesErrorType = export async function getAddresses< chain extends Chain | undefined, account extends Account | undefined = undefined, ->(client: Client): Promise { +>( + client: Client, + { requestOptions }: GetAddressesParameters = {}, +): Promise { if (client.account?.type === 'local') return [client.account.address] const addresses = await client.request( { method: 'eth_accounts' }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) return addresses.map((address) => checksumAddress(address)) } diff --git a/src/actions/wallet/getCallsStatus.ts b/src/actions/wallet/getCallsStatus.ts index 249c9698bb..8e8cdd1118 100644 --- a/src/actions/wallet/getCallsStatus.ts +++ b/src/actions/wallet/getCallsStatus.ts @@ -4,7 +4,10 @@ import type { ErrorType } from '../../errors/utils.js' import type { Account } from '../../types/account.js' import type { ExtractCapabilities } from '../../types/capabilities.js' import type { Chain } from '../../types/chain.js' -import type { WalletGetCallsStatusReturnType } from '../../types/eip1193.js' +import type { + EIP1193RequestOptions, + WalletGetCallsStatusReturnType, +} from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { RpcTransactionReceipt } from '../../types/rpc.js' import type { Prettify } from '../../types/utils.js' @@ -18,7 +21,11 @@ import { fallbackTransactionErrorMagicIdentifier, } from './sendCalls.js' -export type GetCallsStatusParameters = { id: string } +export type GetCallsStatusParameters = { + id: string + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} export type GetCallsStatusReturnType = Prettify< Omit< @@ -64,6 +71,7 @@ export async function getCallsStatus< client: Client, parameters: GetCallsStatusParameters, ): Promise { + const { requestOptions } = parameters async function getStatus(id: Hex) { const isTransactions = id.endsWith(fallbackMagicIdentifier.slice(2)) if (isTransactions) { @@ -80,7 +88,7 @@ export async function getCallsStatus< method: 'eth_getTransactionReceipt', params: [`0x${hash}`], }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) : undefined, ), @@ -101,10 +109,13 @@ export async function getCallsStatus< version: '2.0.0', } } - return client.request({ - method: 'wallet_getCallsStatus', - params: [id], - }) + return client.request( + { + method: 'wallet_getCallsStatus', + params: [id], + }, + requestOptions, + ) } const { diff --git a/src/actions/wallet/getCapabilities.ts b/src/actions/wallet/getCapabilities.ts index 7977ccd483..f73c3cc925 100644 --- a/src/actions/wallet/getCapabilities.ts +++ b/src/actions/wallet/getCapabilities.ts @@ -10,6 +10,7 @@ import type { ChainIdToCapabilities, ExtractCapabilities, } from '../../types/capabilities.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Prettify } from '../../types/utils.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { numberToHex } from '../../utils/encoding/toHex.js' @@ -19,6 +20,8 @@ export type GetCapabilitiesParameters< > = { account?: Account | Address | undefined chainId?: chainId | number | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type GetCapabilitiesReturnType< @@ -60,17 +63,20 @@ export async function getCapabilities< client: Client, parameters: GetCapabilitiesParameters = {}, ): Promise> { - const { account = client.account, chainId } = parameters + const { account = client.account, chainId, requestOptions } = parameters const account_ = account ? parseAccount(account) : undefined const params = chainId ? ([account_?.address, [numberToHex(chainId)]] as const) : ([account_?.address] as const) - const capabilities_raw = await client.request({ - method: 'wallet_getCapabilities', - params, - }) + const capabilities_raw = await client.request( + { + method: 'wallet_getCapabilities', + params, + }, + requestOptions, + ) const capabilities = {} as ChainIdToCapabilities< ExtractCapabilities<'getCapabilities', 'ReturnType'>, diff --git a/src/actions/wallet/getPermissions.ts b/src/actions/wallet/getPermissions.ts index a03c913d87..9e5f493a11 100644 --- a/src/actions/wallet/getPermissions.ts +++ b/src/actions/wallet/getPermissions.ts @@ -3,9 +3,17 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' -import type { WalletPermission } from '../../types/eip1193.js' +import type { + EIP1193RequestOptions, + WalletPermission, +} from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' +export type GetPermissionsParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type GetPermissionsReturnType = WalletPermission[] export type GetPermissionsErrorType = RequestErrorType | ErrorType @@ -33,10 +41,14 @@ export type GetPermissionsErrorType = RequestErrorType | ErrorType export async function getPermissions< chain extends Chain | undefined, account extends Account | undefined = undefined, ->(client: Client) { +>( + client: Client, + parameters: GetPermissionsParameters = {}, +) { + const { requestOptions } = parameters const permissions = await client.request( { method: 'wallet_getPermissions' }, - { dedupe: true }, + { dedupe: true, ...requestOptions }, ) return permissions } diff --git a/src/actions/wallet/prepareAuthorization.ts b/src/actions/wallet/prepareAuthorization.ts index 8cbd0d0dda..d6811c4267 100644 --- a/src/actions/wallet/prepareAuthorization.ts +++ b/src/actions/wallet/prepareAuthorization.ts @@ -17,6 +17,7 @@ import type { AuthorizationRequest, } from '../../types/authorization.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { PartialBy } from '../../types/utils.js' import { isAddressEqual } from '../../utils/address/isAddressEqual.js' import type { RequestErrorType } from '../../utils/buildRequest.js' @@ -35,6 +36,8 @@ export type PrepareAuthorizationParameters< * be executed by another Account. */ executor?: 'self' | Account | Address | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type PrepareAuthorizationReturnType = Authorization @@ -93,7 +96,12 @@ export async function prepareAuthorization< client: Client, parameters: PrepareAuthorizationParameters, ): Promise { - const { account: account_ = client.account, chainId, nonce } = parameters + const { + account: account_ = client.account, + chainId, + nonce, + requestOptions, + } = parameters if (!account_) throw new AccountNotFoundError({ @@ -116,7 +124,7 @@ export async function prepareAuthorization< if (typeof authorization.chainId === 'undefined') authorization.chainId = client.chain?.id ?? - (await getAction(client, getChainId, 'getChainId')({})) + (await getAction(client, getChainId, 'getChainId')({ requestOptions })) if (typeof authorization.nonce === 'undefined') { authorization.nonce = await getAction( @@ -126,6 +134,7 @@ export async function prepareAuthorization< )({ address: account.address, blockTag: 'pending', + requestOptions, }) if ( executor === 'self' || diff --git a/src/actions/wallet/prepareTransactionRequest.ts b/src/actions/wallet/prepareTransactionRequest.ts index f6a686a0ab..766404a6c0 100644 --- a/src/actions/wallet/prepareTransactionRequest.ts +++ b/src/actions/wallet/prepareTransactionRequest.ts @@ -36,6 +36,7 @@ import type { DeriveChain, GetChainParameter, } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { GetTransactionRequestKzgParameter } from '../../types/kzg.js' import type { TransactionRequest, @@ -140,7 +141,11 @@ export type PrepareTransactionRequestParameters< > = request & GetAccountParameter & GetChainParameter & - GetTransactionRequestKzgParameter & { chainId?: number | undefined } + GetTransactionRequestKzgParameter & { + chainId?: number | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined + } export type PrepareTransactionRequestReturnType< chain extends Chain | undefined = Chain | undefined, @@ -277,6 +282,7 @@ export async function prepareTransactionRequest< chain = client.chain, nonceManager, parameters, + requestOptions, } = request const prepareTransactionRequest = (() => { @@ -455,7 +461,7 @@ export async function prepareTransactionRequest< client, getBlock_, 'getBlock', - )({ blockTag: 'latest' }) + )({ blockTag: 'latest', requestOptions }) return block } @@ -472,6 +478,7 @@ export async function prepareTransactionRequest< )({ address: account.address, blockTag: 'pending', + requestOptions, }) if ( @@ -537,6 +544,7 @@ export async function prepareTransactionRequest< block: block as Block, chain, request: request as PrepareTransactionRequestParameters, + requestOptions, }) if ( @@ -568,6 +576,7 @@ export async function prepareTransactionRequest< chain, request: request as PrepareTransactionRequestParameters, type: 'legacy', + requestOptions, }, ) request.gasPrice = gasPrice_ @@ -584,6 +593,7 @@ export async function prepareTransactionRequest< ...request, account, prepare: account?.type === 'local' ? [] : ['blobVersionedHashes'], + requestOptions, } as EstimateGasParameters) if ( diff --git a/src/actions/wallet/requestAddresses.ts b/src/actions/wallet/requestAddresses.ts index d801784487..e8d14e8838 100644 --- a/src/actions/wallet/requestAddresses.ts +++ b/src/actions/wallet/requestAddresses.ts @@ -5,9 +5,15 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import { getAddress } from '../../utils/address/getAddress.js' import type { RequestErrorType } from '../../utils/buildRequest.js' +export type RequestAddressesParameters = { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} + export type RequestAddressesReturnType = Address[] export type RequestAddressesErrorType = RequestErrorType | ErrorType @@ -23,6 +29,7 @@ export type RequestAddressesErrorType = RequestErrorType | ErrorType * This API can be useful for dapps that need to access the user's accounts in order to execute transactions or interact with smart contracts. * * @param client - Client to use + * @param parameters - {@link RequestAddressesParameters} * @returns List of accounts managed by a wallet {@link RequestAddressesReturnType} * * @example @@ -41,10 +48,11 @@ export async function requestAddresses< account extends Account | undefined = undefined, >( client: Client, + { requestOptions }: RequestAddressesParameters = {}, ): Promise { const addresses = await client.request( { method: 'eth_requestAccounts' }, - { dedupe: true, retryCount: 0 }, + { dedupe: true, retryCount: 0, ...requestOptions }, ) return addresses.map((address) => getAddress(address)) } diff --git a/src/actions/wallet/sendRawTransaction.ts b/src/actions/wallet/sendRawTransaction.ts index f5a5f4c1cb..0b9f23364b 100644 --- a/src/actions/wallet/sendRawTransaction.ts +++ b/src/actions/wallet/sendRawTransaction.ts @@ -2,6 +2,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hash } from '../../types/misc.js' import type { TransactionSerializedGeneric } from '../../types/transaction.js' import type { RequestErrorType } from '../../utils/buildRequest.js' @@ -9,6 +10,8 @@ import type { RequestErrorType } from '../../utils/buildRequest.js' export type SendRawTransactionParameters = { /** The signed serialized transaction. */ serializedTransaction: TransactionSerializedGeneric + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type SendRawTransactionReturnType = Hash @@ -41,13 +44,13 @@ export type SendRawTransactionErrorType = RequestErrorType | ErrorType */ export async function sendRawTransaction( client: Client, - { serializedTransaction }: SendRawTransactionParameters, + { serializedTransaction, requestOptions }: SendRawTransactionParameters, ): Promise { return client.request( { method: 'eth_sendRawTransaction', params: [serializedTransaction], }, - { retryCount: 0 }, + { retryCount: 0, ...requestOptions }, ) } diff --git a/src/actions/wallet/sendTransaction.ts b/src/actions/wallet/sendTransaction.ts index 973ccc3f77..3502fcfbb7 100644 --- a/src/actions/wallet/sendTransaction.ts +++ b/src/actions/wallet/sendTransaction.ts @@ -22,6 +22,7 @@ import type { DeriveChain, GetChainParameter, } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { GetTransactionRequestKzgParameter } from '../../types/kzg.js' import type { Hash } from '../../types/misc.js' import type { TransactionRequest } from '../../types/transaction.js' @@ -83,7 +84,10 @@ export type SendTransactionParameters< > = request & GetAccountParameter & GetChainParameter & - GetTransactionRequestKzgParameter + GetTransactionRequestKzgParameter & { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined + } export type SendTransactionReturnType = Hash @@ -170,6 +174,7 @@ export async function sendTransaction< maxFeePerGas, maxPriorityFeePerGas, nonce, + requestOptions, type, value, ...rest @@ -253,7 +258,7 @@ export async function sendTransaction< method, params: [request], }, - { retryCount: 0 }, + { retryCount: 0, ...requestOptions }, ) } catch (e) { if (isWalletNamespaceSupported === false) throw e @@ -273,7 +278,7 @@ export async function sendTransaction< method: 'wallet_sendTransaction', params: [request], }, - { retryCount: 0 }, + { retryCount: 0, ...requestOptions }, ) .then((hash) => { supportsWalletNamespace.set(client.uid, true) diff --git a/src/actions/wallet/switchChain.ts b/src/actions/wallet/switchChain.ts index 8a9784b15d..97ad0aa4a6 100644 --- a/src/actions/wallet/switchChain.ts +++ b/src/actions/wallet/switchChain.ts @@ -3,6 +3,7 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' import { type NumberToHexErrorType, @@ -12,6 +13,8 @@ import { export type SwitchChainParameters = { /** ID of Chain to switch to */ id: Chain['id'] + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } export type SwitchChainErrorType = @@ -42,7 +45,10 @@ export type SwitchChainErrorType = export async function switchChain< chain extends Chain | undefined, account extends Account | undefined = undefined, ->(client: Client, { id }: SwitchChainParameters) { +>( + client: Client, + { id, requestOptions }: SwitchChainParameters, +) { await client.request( { method: 'wallet_switchEthereumChain', @@ -52,6 +58,6 @@ export async function switchChain< }, ], }, - { retryCount: 0 }, + { retryCount: 0, ...requestOptions }, ) } diff --git a/src/actions/wallet/waitForCallsStatus.ts b/src/actions/wallet/waitForCallsStatus.ts index 864d45ec49..e9d35d6d36 100644 --- a/src/actions/wallet/waitForCallsStatus.ts +++ b/src/actions/wallet/waitForCallsStatus.ts @@ -4,6 +4,7 @@ import { BaseError } from '../../errors/base.js' import { BundleFailedError } from '../../errors/calls.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import { getAction } from '../../utils/getAction.js' import { type ObserveErrorType, observe } from '../../utils/observe.js' import { type PollErrorType, poll } from '../../utils/poll.js' @@ -30,6 +31,10 @@ export type WaitForCallsStatusParameters = { * @default client.pollingInterval */ pollingInterval?: number | undefined + /** + * Request options. + */ + requestOptions?: EIP1193RequestOptions | undefined /** * Number of times to retry if the call bundle failed. * @default 4 (exponential backoff) @@ -98,6 +103,7 @@ export async function waitForCallsStatus( const { id, pollingInterval = client.pollingInterval, + requestOptions, status = ({ statusCode }) => statusCode === 200 || statusCode >= 300, retryCount = 4, retryDelay = ({ count }) => ~~(1 << count) * 200, // exponential backoff @@ -128,7 +134,7 @@ export async function waitForCallsStatus( client, getCallsStatus, 'getCallsStatus', - )({ id }) + )({ id, requestOptions }) if (throwOnFailure && result.status === 'failure') throw new BundleFailedError(result) return result @@ -136,6 +142,7 @@ export async function waitForCallsStatus( { retryCount, delay: retryDelay, + signal: requestOptions?.signal, }, ) if (!status(result)) return diff --git a/src/actions/wallet/watchAsset.ts b/src/actions/wallet/watchAsset.ts index f4d86e8baa..1d50e22ce4 100644 --- a/src/actions/wallet/watchAsset.ts +++ b/src/actions/wallet/watchAsset.ts @@ -3,10 +3,16 @@ import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' import type { ErrorType } from '../../errors/utils.js' import type { Chain } from '../../types/chain.js' -import type { WatchAssetParams } from '../../types/eip1193.js' +import type { + EIP1193RequestOptions, + WatchAssetParams, +} from '../../types/eip1193.js' import type { RequestErrorType } from '../../utils/buildRequest.js' -export type WatchAssetParameters = WatchAssetParams +export type WatchAssetParameters = WatchAssetParams & { + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined +} export type WatchAssetReturnType = boolean export type WatchAssetErrorType = RequestErrorType | ErrorType @@ -45,12 +51,13 @@ export async function watchAsset< client: Client, params: WatchAssetParameters, ): Promise { + const { requestOptions, ...watchAssetParams } = params const added = await client.request( { method: 'wallet_watchAsset', - params, + params: watchAssetParams, }, - { retryCount: 0 }, + { retryCount: 0, ...requestOptions }, ) return added } diff --git a/src/actions/wallet/writeContract.ts b/src/actions/wallet/writeContract.ts index f98564abdc..184974be46 100644 --- a/src/actions/wallet/writeContract.ts +++ b/src/actions/wallet/writeContract.ts @@ -24,6 +24,7 @@ import type { ContractFunctionName, ContractFunctionParameters, } from '../../types/contract.js' +import type { EIP1193RequestOptions } from '../../types/eip1193.js' import type { Hex } from '../../types/misc.js' import type { Prettify, UnionEvaluate, UnionOmit } from '../../types/utils.js' import { @@ -82,6 +83,8 @@ export type WriteContractParameters< > & { /** Data to append to the end of the calldata. Useful for adding a ["domain" tag](https://opensea.notion.site/opensea/Seaport-Order-Attributions-ec2d69bf455041a5baa490941aad307f). */ dataSuffix?: Hex | undefined + /** Request options. */ + requestOptions?: EIP1193RequestOptions | undefined } > & UnionEvaluate< diff --git a/src/clients/transports/http.test.ts b/src/clients/transports/http.test.ts index db3dad5c58..77cdb81449 100644 --- a/src/clients/transports/http.test.ts +++ b/src/clients/transports/http.test.ts @@ -610,3 +610,81 @@ test('no url', () => { `, ) }) + +describe('request cancellation', () => { + test('cancels request with AbortSignal', async () => { + const server = await createHttpServer((_, res) => { + // Delay response to allow time for cancellation + setTimeout(() => { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ jsonrpc: '2.0', result: '0x1', id: 0 })) + }, 100) + }) + + const controller = new AbortController() + const transport = http(server.url)({}) + + // Cancel after 50ms (before server responds at 100ms) + setTimeout(() => controller.abort(), 50) + + await expect( + transport.request( + { method: 'eth_blockNumber' }, + { signal: controller.signal }, + ), + ).rejects.toThrow() + + await server.close() + }) + + test('successful request with signal', async () => { + const server = await createHttpServer((_, res) => { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ jsonrpc: '2.0', result: '0x1', id: 0 })) + }) + + const controller = new AbortController() + const transport = http(server.url)({}) + + const result = await transport.request( + { method: 'eth_blockNumber' }, + { signal: controller.signal }, + ) + + expect(result).toBe('0x1') + await server.close() + }) + + test('multiple requests with same controller', async () => { + const server = await createHttpServer((_, res) => { + setTimeout(() => { + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ jsonrpc: '2.0', result: '0x1', id: 0 })) + }, 100) + }) + + const controller = new AbortController() + const transport = http(server.url)({}) + + // Start multiple requests + const promise1 = transport.request( + { method: 'eth_blockNumber' }, + { signal: controller.signal }, + ) + const promise2 = transport.request( + { + method: 'eth_getBalance', + params: ['0x0000000000000000000000000000000000000000'], + }, + { signal: controller.signal }, + ) + + // Cancel after 50ms + setTimeout(() => controller.abort(), 50) + + await expect(promise1).rejects.toThrow() + await expect(promise2).rejects.toThrow() + + await server.close() + }) +}) diff --git a/src/clients/transports/http.ts b/src/clients/transports/http.ts index e6710ea9be..1fb295005a 100644 --- a/src/clients/transports/http.ts +++ b/src/clients/transports/http.ts @@ -125,7 +125,7 @@ export function http< key, methods, name, - async request({ method, params }) { + async request({ method, params }, options) { const body = { method, params } const { schedule } = createBatchScheduler({ @@ -137,6 +137,7 @@ export function http< fn: (body: RpcRequest[]) => rpcClient.request({ body, + fetchOptions: { signal: options?.signal ?? null }, }), sort: (a, b) => a.id - b.id, }) @@ -147,6 +148,7 @@ export function http< : [ await rpcClient.request({ body, + fetchOptions: { signal: options?.signal ?? null }, }), ] diff --git a/src/errors/utils.ts b/src/errors/utils.ts index c718af9230..062977e91b 100644 --- a/src/errors/utils.ts +++ b/src/errors/utils.ts @@ -1,6 +1,7 @@ import type { Address } from 'abitype' export type ErrorType = Error & { name: name } +export type AbortErrorType = ErrorType<'AbortError'> export const getContractAddress = (address: Address) => address export const getUrl = (url: string) => url diff --git a/src/types/eip1193.ts b/src/types/eip1193.ts index 9dc31c49b6..20c9f6edc6 100644 --- a/src/types/eip1193.ts +++ b/src/types/eip1193.ts @@ -2138,6 +2138,8 @@ export type EIP1193RequestOptions = { retryDelay?: number | undefined /** The max number of times to retry. */ retryCount?: number | undefined + /** AbortSignal to cancel the request. */ + signal?: AbortSignal | undefined /** Unique identifier for the request. */ uid?: string | undefined } diff --git a/src/utils/buildRequest.test.ts b/src/utils/buildRequest.test.ts index 10c4dac8f2..d4d305b786 100644 --- a/src/utils/buildRequest.test.ts +++ b/src/utils/buildRequest.test.ts @@ -41,12 +41,13 @@ import { getHttpRpcClient } from './rpc/http.js' function request(url: string) { const httpClient = getHttpRpcClient(url) - return async ({ method, params }: any) => { + return async ({ method, params }: any, options?: any) => { const { error, result } = await httpClient.request({ body: { method, params, }, + fetchOptions: { signal: options.signal }, }) if (error) throw new RpcRequestError({ @@ -254,6 +255,79 @@ describe('args', () => { }) }) +describe('options', () => { + test('passes signal to underlying request', async () => { + let receivedSignal: AbortSignal | undefined + const mockRequest = async (_args: any, options?: any) => { + receivedSignal = options?.signal + return 'success' + } + + const controller = new AbortController() + const request_ = buildRequest(mockRequest) + + await request_({ method: 'eth_blockNumber' }, { signal: controller.signal }) + + expect(receivedSignal).toBe(controller.signal) + }) + + test('passes other options alongside signal', async () => { + let receivedOptions: any + const mockRequest = async (_args: any, options?: any) => { + receivedOptions = options + return 'success' + } + + const controller = new AbortController() + const request_ = buildRequest(mockRequest) + + await request_( + { method: 'eth_blockNumber' }, + { + signal: controller.signal, + retryCount: 1, + dedupe: true, + }, + ) + + expect(receivedOptions).toEqual({ signal: controller.signal }) + }) + + test('works without signal', async () => { + let receivedOptions: any + const mockRequest = async (_args: any, options?: any) => { + receivedOptions = options + return 'success' + } + + const request_ = buildRequest(mockRequest) + + await request_({ method: 'eth_blockNumber' }, { dedupe: true }) + + expect(receivedOptions).toBeUndefined() + }) + + test('prioritizes override options over initial options', async () => { + let receivedSignal: AbortSignal | undefined + const mockRequest = async (_args: any, options?: any) => { + receivedSignal = options?.signal + return 'success' + } + + const controller1 = new AbortController() + const controller2 = new AbortController() + + const request_ = buildRequest(mockRequest, { signal: controller1.signal }) + + await request_( + { method: 'eth_blockNumber' }, + { signal: controller2.signal }, + ) + + expect(receivedSignal).toBe(controller2.signal) + }) +}) + describe('behavior', () => { describe('error types', () => { test('BaseError', async () => { diff --git a/src/utils/buildRequest.ts b/src/utils/buildRequest.ts index 719761f859..0e5e045c51 100644 --- a/src/utils/buildRequest.ts +++ b/src/utils/buildRequest.ts @@ -64,7 +64,7 @@ import { UserRejectedRequestError, type UserRejectedRequestErrorType, } from '../errors/rpc.js' -import type { ErrorType } from '../errors/utils.js' +import type { AbortErrorType, ErrorType } from '../errors/utils.js' import type { EIP1193RequestFn, EIP1193RequestOptions, @@ -111,18 +111,22 @@ export type RequestErrorType = | UserRejectedRequestErrorType | WebSocketRequestErrorType | WithRetryErrorType + | AbortErrorType | ErrorType -export function buildRequest Promise>( - request: request, - options: EIP1193RequestOptions = {}, -): EIP1193RequestFn { - return async (args, overrideOptions = {}) => { +export function buildRequest< + request extends ( + args: any, + options?: EIP1193RequestOptions | undefined, + ) => Promise, +>(request: request, options: EIP1193RequestOptions = {}): EIP1193RequestFn { + return async (args, overrideOptions: EIP1193RequestOptions = {}) => { const { dedupe = false, methods, retryDelay = 150, retryCount = 3, + signal, uid, } = { ...options, @@ -147,7 +151,7 @@ export function buildRequest Promise>( withRetry( async () => { try { - return await request(args) + return await request(args, signal ? { signal } : undefined) } catch (err_) { const err = err_ as unknown as RpcError< RpcErrorCode | ProviderRpcErrorCode @@ -161,7 +165,9 @@ export function buildRequest Promise>( throw new InvalidRequestRpcError(err) // -32601 case MethodNotFoundRpcError.code: - throw new MethodNotFoundRpcError(err, { method: args.method }) + throw new MethodNotFoundRpcError(err, { + method: args.method, + }) // -32602 case InvalidParamsRpcError.code: throw new InvalidParamsRpcError(err) diff --git a/src/utils/promise/withRetry.test.ts b/src/utils/promise/withRetry.test.ts index 43ff179bf3..729fdfe002 100644 --- a/src/utils/promise/withRetry.test.ts +++ b/src/utils/promise/withRetry.test.ts @@ -154,3 +154,69 @@ test('delay: fn', async () => { ).rejects.toThrowError('test') expect(end > 1000 && end < 1020).toBe(true) }) + +test('signal: already aborted', async () => { + let retryTimes = 0 + const controller = new AbortController() + controller.abort() + + await expect( + withRetry( + async () => { + retryTimes++ + throw new Error('test') + }, + { signal: controller.signal }, + ), + ).rejects.toThrow('This operation was aborted') + expect(retryTimes).toBe(0) +}) + +test('signal: aborted during retries', async () => { + let retryTimes = 0 + const controller = new AbortController() + + await expect( + withRetry( + async () => { + retryTimes++ + if (retryTimes === 1) controller.abort() + throw new Error('test') + }, + { signal: controller.signal, delay: 0 }, + ), + ).rejects.toThrow('This operation was aborted') + expect(retryTimes).toBe(1) +}) + +test('signal: not aborted', async () => { + let retryTimes = 0 + const controller = new AbortController() + + const result = await withRetry( + async () => { + retryTimes++ + if (retryTimes < 2) throw new Error('test') + return 'success' + }, + { signal: controller.signal, delay: 0 }, + ) + + expect(result).toBe('success') + expect(retryTimes).toBe(2) +}) + +test('signal: aborted with custom reason', async () => { + const controller = new AbortController() + const customError = new Error('Custom abort reason') + controller.abort(customError) + + await expect( + withRetry( + async () => { + throw new Error('test') + }, + { signal: controller.signal }, + ), + ).rejects.toThrow('Custom abort reason') +}) diff --git a/src/utils/promise/withRetry.ts b/src/utils/promise/withRetry.ts index c18ebc2948..50785d628a 100644 --- a/src/utils/promise/withRetry.ts +++ b/src/utils/promise/withRetry.ts @@ -19,6 +19,8 @@ export type WithRetryParameters = { error: Error }) => Promise | boolean) | undefined + // AbortSignal to cancel retries. + signal?: AbortSignal | undefined } export type WithRetryErrorType = ErrorType @@ -29,10 +31,25 @@ export function withRetry( delay: delay_ = 100, retryCount = 2, shouldRetry = () => true, + signal, }: WithRetryParameters = {}, ) { return new Promise((resolve, reject) => { + const rejectWithAbort = () => { + reject(signal?.reason ?? new Error('Aborted')) + } + + if (signal?.aborted) { + rejectWithAbort() + return + } + const attemptRetry = async ({ count = 0 } = {}) => { + if (signal?.aborted) { + rejectWithAbort() + return + } + const retry = async ({ error }: { error: Error }) => { const delay = typeof delay_ === 'function' ? delay_({ count, error }) : delay_