viem
Version:
195 lines (185 loc) • 5.78 kB
text/typescript
import type { Address, Narrow } from 'abitype'
import {
type SendTransactionErrorType,
sendTransaction,
} from '../../../actions/wallet/sendTransaction.js'
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { BaseError } from '../../../errors/base.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Batches, Calls } from '../../../types/calls.js'
import type {
Chain,
DeriveChain,
GetChainParameter,
} from '../../../types/chain.js'
import type { Hex } from '../../../types/misc.js'
import type { UnionEvaluate, UnionPick } from '../../../types/utils.js'
import type { FormattedTransactionRequest } from '../../../utils/formatters/transactionRequest.js'
import { withCache } from '../../../utils/promise/withCache.js'
import { ExecuteUnsupportedError } from '../errors.js'
import {
type EncodeExecuteBatchesDataErrorType,
encodeExecuteBatchesData,
} from '../utils/encodeExecuteBatchesData.js'
import {
type GetExecuteErrorReturnType,
getExecuteError,
} from '../utils/getExecuteError.js'
import { supportsExecutionMode } from './supportsExecutionMode.js'
/** @internal */
export type Batch = { calls: readonly unknown[]; opData?: Hex | undefined }
export type ExecuteBatchesParameters<
batches extends readonly Batch[] = readonly Batch[],
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
> = UnionEvaluate<
UnionPick<
FormattedTransactionRequest<_derivedChain>,
| 'authorizationList'
| 'gas'
| 'gasPrice'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
>
> &
GetAccountParameter<account, Account | Address, true, true> &
GetChainParameter<chain, chainOverride> & {
/** Address that will execute the calls. */
address: Address
/** Batches to execute. */
batches: Batches<Narrow<batches>, { opData?: Hex | undefined }>
}
export type ExecuteBatchesReturnType = Hex
export type ExecuteBatchesErrorType =
| GetExecuteErrorReturnType
| EncodeExecuteBatchesDataErrorType
| SendTransactionErrorType
| ErrorType
/**
* Executes batches of call(s) using "batch of batches" mode on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { executeBatches } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await executeBatches(client, {
* account,
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http, parseEther } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { executeBatches } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await executeBatches(client, {
* batches: [
* {
* calls: [
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: parseEther('1'),
* },
* ],
* },
* {
* calls: [
* {
* to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
* value: parseEther('2'),
* },
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* ],
* },
* ],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteBatchesParameters}
* @returns Transaction hash. {@link ExecuteBatchesReturnType}
*/
export async function executeBatches<
batches extends readonly Batch[],
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: ExecuteBatchesParameters<batches, chain, account, chainOverride>,
): Promise<ExecuteBatchesReturnType> {
const { authorizationList, batches } = parameters
const address = authorizationList?.[0]?.address ?? parameters.address
const supported = await withCache(
() =>
supportsExecutionMode(client, {
address,
mode: 'batchOfBatches',
}),
{
cacheKey: `supportsExecutionMode.${client.uid}.${address}.batchOfBatches`,
},
)
if (!supported) throw new ExecuteUnsupportedError()
try {
return await sendTransaction(client, {
...parameters,
to: parameters.address,
data: encodeExecuteBatchesData({ batches }),
} as any)
} catch (e) {
const calls = batches.flatMap((b) => b.calls) as Calls<Narrow<batches>>
throw getExecuteError(e as BaseError, { calls })
}
}