FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,171 @@
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 { 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 { executionMode } from '../constants.js'
import { ExecuteUnsupportedError } from '../errors.js'
import {
type EncodeExecuteDataErrorType,
encodeExecuteData,
} from '../utils/encodeExecuteData.js'
import {
type GetExecuteErrorReturnType,
getExecuteError,
} from '../utils/getExecuteError.js'
import { supportsExecutionMode } from './supportsExecutionMode.js'
export type ExecuteParameters<
calls extends readonly unknown[] = readonly unknown[],
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
/** Calls to execute. */
calls: Calls<Narrow<calls>>
/** Additional data to include for execution. */
opData?: Hex | undefined
}
export type ExecuteReturnType = Hex
export type ExecuteErrorType =
| GetExecuteErrorReturnType
| EncodeExecuteDataErrorType
| SendTransactionErrorType
| ErrorType
/**
* Executes call(s) using the `execute` function on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { execute } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await execute(client, {
* account,
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { execute } from 'viem/experimental/erc7821'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* account,
* chain: mainnet,
* transport: http(),
* })
*
* const hash = await execute(client, {
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteParameters}
* @returns Transaction hash. {@link ExecuteReturnType}
*/
export async function execute<
const calls extends readonly unknown[],
chain extends Chain | undefined,
account extends Account | undefined,
chainOverride extends Chain | undefined = undefined,
>(
client: Client<Transport, chain, account>,
parameters: ExecuteParameters<calls, chain, account, chainOverride>,
): Promise<ExecuteReturnType> {
const { authorizationList, calls, opData } = parameters
const address = authorizationList?.[0]?.address ?? parameters.address
const mode = opData ? executionMode.opData : executionMode.default
const supported = await withCache(
() =>
supportsExecutionMode(client, {
address,
mode,
}),
{
cacheKey: `supportsExecutionMode.${client.uid}.${address}.${mode}`,
},
)
if (!supported) throw new ExecuteUnsupportedError()
try {
return await sendTransaction(client, {
...parameters,
to: parameters.address,
data: encodeExecuteData({ calls, opData }),
} as any)
} catch (e) {
throw getExecuteError(e as BaseError, { calls })
}
}

View File

@@ -0,0 +1,194 @@
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 })
}
}

View File

@@ -0,0 +1,73 @@
import type { Address } from '../../../accounts/index.js'
import { readContract } from '../../../actions/public/readContract.js'
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 { Hex } from '../../../types/misc.js'
import { withCache } from '../../../utils/promise/withCache.js'
import { abi, executionMode } from '../constants.js'
export type SupportsExecutionModeParameters = {
address: Address
mode?: 'default' | 'opData' | 'batchOfBatches' | Hex
}
export type SupportsExecutionModeReturnType = boolean
export type SupportsExecutionModeErrorType = ErrorType
const toSerializedMode = {
default: executionMode.default,
opData: executionMode.opData,
batchOfBatches: executionMode.batchOfBatches,
} as const
/**
* Checks if the contract supports the ERC-7821 execution mode.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { supportsExecutionMode } from 'viem/experimental/erc7821'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* })
*
* const supported = await supportsExecutionMode(client, {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link SupportsExecutionModeParameters}
* @returns If the execution mode is supported. {@link SupportsExecutionModeReturnType}
*/
export async function supportsExecutionMode<
chain extends Chain | undefined = Chain | undefined,
>(
client: Client<Transport, chain>,
parameters: SupportsExecutionModeParameters,
): Promise<SupportsExecutionModeReturnType> {
const { address, mode: m = 'default' } = parameters
const mode = m.startsWith('0x') ? m : (toSerializedMode as any)[m]
try {
return await withCache(
() =>
readContract(client, {
abi,
address,
functionName: 'supportsExecutionMode',
args: [mode],
}),
{
cacheKey: `supportsExecutionMode.${address}.${mode}`,
},
)
} catch {
return false
}
}

64
node_modules/viem/experimental/erc7821/constants.ts generated vendored Normal file
View File

@@ -0,0 +1,64 @@
export const abi = [
{
type: 'fallback',
stateMutability: 'payable',
},
{
type: 'receive',
stateMutability: 'payable',
},
{
type: 'function',
name: 'execute',
inputs: [
{
name: 'mode',
type: 'bytes32',
internalType: 'bytes32',
},
{
name: 'executionData',
type: 'bytes',
internalType: 'bytes',
},
],
outputs: [],
stateMutability: 'payable',
},
{
type: 'function',
name: 'supportsExecutionMode',
inputs: [
{
name: 'mode',
type: 'bytes32',
internalType: 'bytes32',
},
],
outputs: [
{
name: 'result',
type: 'bool',
internalType: 'bool',
},
],
stateMutability: 'view',
},
{
type: 'error',
name: 'FnSelectorNotRecognized',
inputs: [],
},
{
type: 'error',
name: 'UnsupportedExecutionMode',
inputs: [],
},
] as const
export const executionMode = {
default: '0x0100000000000000000000000000000000000000000000000000000000000000',
opData: '0x0100000000007821000100000000000000000000000000000000000000000000',
batchOfBatches:
'0x0100000000007821000200000000000000000000000000000000000000000000',
} as const

View File

@@ -0,0 +1,258 @@
import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { Account } from '../../../types/account.js'
import type { Chain } from '../../../types/chain.js'
import {
type ExecuteParameters,
type ExecuteReturnType,
execute,
} from '../actions/execute.js'
import {
type Batch,
type ExecuteBatchesParameters,
type ExecuteBatchesReturnType,
executeBatches,
} from '../actions/executeBatches.js'
import {
type SupportsExecutionModeParameters,
type SupportsExecutionModeReturnType,
supportsExecutionMode,
} from '../actions/supportsExecutionMode.js'
export type Erc7821Actions<
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
> = {
/**
* Executes call(s) using the `execute` function on an [ERC-7821-compatible contract](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.execute({
* account,
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @example
* ```ts
* // Account Hoisting
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* account,
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.execute({
* calls: [{
* {
* data: '0xdeadbeef',
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* },
* {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* value: 69420n,
* },
* }],
* to: account.address,
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link ExecuteParameters}
* @returns Transaction hash. {@link ExecuteReturnType}
*/
execute: <
const calls extends readonly unknown[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: ExecuteParameters<calls, chain, account, chainOverride>,
) => Promise<ExecuteReturnType>
/**
* 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 { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.executeBatches({
* 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 { erc7821Actions } from 'viem/experimental'
*
* const account = privateKeyToAccount('0x...')
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const hash = await client.executeBatches({
* 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}
*/
executeBatches: <
const batches extends readonly Batch[],
chainOverride extends Chain | undefined = undefined,
>(
parameters: ExecuteBatchesParameters<
batches,
chain,
account,
chainOverride
>,
) => Promise<ExecuteBatchesReturnType>
/**
* Checks if the contract supports the ERC-7821 execution mode.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*
* const supported = await supportsExecutionMode(client, {
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
* })
* ```
*
* @param client - Client to use.
* @param parameters - {@link SupportsExecutionModeParameters}
* @returns If the execution mode is supported. {@link SupportsExecutionModeReturnType}
*/
supportsExecutionMode: (
parameters: SupportsExecutionModeParameters,
) => Promise<SupportsExecutionModeReturnType>
}
/**
* A suite of Actions for [ERC-7821](https://eips.ethereum.org/EIPS/eip-7821).
*
* @example
* import { createClient, http } from 'viem'
* import { mainnet } from 'viem/chains'
* import { erc7821Actions } from 'viem/experimental'
*
* const client = createClient({
* chain: mainnet,
* transport: http(),
* }).extend(erc7821Actions())
*/
export function erc7821Actions() {
return <
transport extends Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(
client: Client<transport, chain, account>,
): Erc7821Actions<chain, account> => {
return {
execute: (parameters) => execute(client, parameters),
executeBatches: (parameters) => executeBatches(client, parameters),
supportsExecutionMode: (parameters) =>
supportsExecutionMode(client, parameters),
}
}
}

29
node_modules/viem/experimental/erc7821/errors.ts generated vendored Normal file
View File

@@ -0,0 +1,29 @@
import { BaseError } from '../../errors/base.js'
export type ExecuteUnsupportedErrorType = ExecuteUnsupportedError & {
name: 'ExecuteUnsupportedError'
}
export class ExecuteUnsupportedError extends BaseError {
constructor() {
super('ERC-7821 execution is not supported.', {
name: 'ExecuteUnsupportedError',
})
}
}
export type FunctionSelectorNotRecognizedErrorType =
FunctionSelectorNotRecognizedError & {
name: 'FunctionSelectorNotRecognizedError'
}
export class FunctionSelectorNotRecognizedError extends BaseError {
constructor() {
super('Function is not recognized.', {
metaMessages: [
'This could be due to any of the following:',
' - The contract does not have the function,',
' - The address is not a contract.',
],
name: 'FunctionSelectorNotRecognizedError',
})
}
}

48
node_modules/viem/experimental/erc7821/index.ts generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// biome-ignore lint/performance/noBarrelFile: entrypoint
export {
type ExecuteErrorType,
type ExecuteParameters,
type ExecuteReturnType,
execute,
} from './actions/execute.js'
export {
type ExecuteBatchesErrorType,
type ExecuteBatchesParameters,
type ExecuteBatchesReturnType,
executeBatches,
} from './actions/executeBatches.js'
export {
type SupportsExecutionModeErrorType,
type SupportsExecutionModeParameters,
type SupportsExecutionModeReturnType,
supportsExecutionMode,
} from './actions/supportsExecutionMode.js'
export { type Erc7821Actions, erc7821Actions } from './decorators/erc7821.js'
export {
ExecuteUnsupportedError,
type ExecuteUnsupportedErrorType,
FunctionSelectorNotRecognizedError,
type FunctionSelectorNotRecognizedErrorType,
} from './errors.js'
export {
type EncodeCallsErrorType,
encodeCalls,
} from './utils/encodeCalls.js'
export {
type EncodeExecuteBatchesDataErrorType,
type EncodeExecuteBatchesDataParameters,
type EncodeExecuteBatchesDataReturnType,
encodeExecuteBatchesData,
} from './utils/encodeExecuteBatchesData.js'
export {
type EncodeExecuteDataErrorType,
type EncodeExecuteDataParameters,
type EncodeExecuteDataReturnType,
encodeExecuteData,
} from './utils/encodeExecuteData.js'
export {
type GetExecuteErrorParameters,
type GetExecuteErrorReturnType,
getExecuteError,
} from './utils/getExecuteError.js'

6
node_modules/viem/experimental/erc7821/package.json generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"type": "module",
"types": "../../_types/experimental/erc7821/index.d.ts",
"module": "../../_esm/experimental/erc7821/index.js",
"main": "../../_cjs/experimental/erc7821/index.js"
}

View File

@@ -0,0 +1,38 @@
import * as AbiParameters from 'ox/AbiParameters'
import type { ErrorType } from '../../../errors/utils.js'
import type { Call, Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
export type EncodeCallsErrorType =
| AbiParameters.encode.ErrorType
| AbiParameters.from.ErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeCalls(
calls_: Calls<readonly unknown[]>,
opData?: Hex | undefined,
) {
const calls = calls_.map((call_) => {
const call = call_ as Call
return {
data: call.abi ? encodeFunctionData(call) : (call.data ?? '0x'),
value: call.value ?? 0n,
target: call.to,
}
})
return AbiParameters.encode(
AbiParameters.from([
'struct Call { address target; uint256 value; bytes data; }',
'Call[] calls',
...(opData ? ['bytes opData'] : []),
]),
[calls, ...(opData ? [opData] : [])] as any,
)
}

View File

@@ -0,0 +1,48 @@
import type { Narrow } from 'abitype'
import * as AbiParameters from 'ox/AbiParameters'
import type { ErrorType } from '../../../errors/utils.js'
import type { Batches } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { abi, executionMode } from '../constants.js'
import { type EncodeCallsErrorType, encodeCalls } from './encodeCalls.js'
/** @internal */
export type Batch = { calls: readonly unknown[]; opData?: Hex | undefined }
export type EncodeExecuteBatchesDataParameters<
batches extends readonly Batch[] = readonly Batch[],
> = {
/** Batches to execute. */
batches: Batches<Narrow<batches>, { opData?: Hex | undefined }>
}
export type EncodeExecuteBatchesDataReturnType = Hex
export type EncodeExecuteBatchesDataErrorType =
| EncodeCallsErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeExecuteBatchesData<batches extends readonly Batch[]>(
parameters: EncodeExecuteBatchesDataParameters<batches>,
): EncodeExecuteBatchesDataReturnType {
const { batches } = parameters
const encodedBatches = AbiParameters.encode(AbiParameters.from('bytes[]'), [
batches.map((b) => {
const batch = b as Batch
return encodeCalls(batch.calls, batch.opData)
}),
])
return encodeFunctionData({
abi,
functionName: 'execute',
args: [executionMode.batchOfBatches, encodedBatches],
})
}

View File

@@ -0,0 +1,42 @@
import type { Narrow } from 'abitype'
import type { ErrorType } from '../../../errors/utils.js'
import type { Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import {
type EncodeFunctionDataErrorType,
encodeFunctionData,
} from '../../../utils/abi/encodeFunctionData.js'
import { abi, executionMode } from '../constants.js'
import { type EncodeCallsErrorType, encodeCalls } from './encodeCalls.js'
export type EncodeExecuteDataParameters<
calls extends readonly unknown[] = readonly unknown[],
> = {
/** Calls to execute. */
calls: Calls<Narrow<calls>>
/** Additional data to include for execution. */
opData?: Hex | undefined
}
export type EncodeExecuteDataReturnType = Hex
export type EncodeExecuteDataErrorType =
| EncodeCallsErrorType
| EncodeFunctionDataErrorType
| ErrorType
export function encodeExecuteData<const calls extends readonly unknown[]>(
parameters: EncodeExecuteDataParameters<calls>,
): EncodeExecuteDataReturnType {
const { calls, opData } = parameters
const encodedCalls = encodeCalls(calls, opData)
const mode = opData ? executionMode.opData : executionMode.default
return encodeFunctionData({
abi,
functionName: 'execute',
args: [mode, encodedCalls],
})
}

View File

@@ -0,0 +1,67 @@
import type { Abi, Narrow } from 'abitype'
import * as AbiError from 'ox/AbiError'
import type { BaseError } from '../../../errors/base.js'
import type { Call, Calls } from '../../../types/calls.js'
import type { Hex } from '../../../types/misc.js'
import { decodeErrorResult } from '../../../utils/abi/decodeErrorResult.js'
import {
type GetContractErrorReturnType,
getContractError,
} from '../../../utils/errors/getContractError.js'
import {
FunctionSelectorNotRecognizedError,
type FunctionSelectorNotRecognizedErrorType,
} from '../errors.js'
export type GetExecuteErrorParameters<
calls extends readonly unknown[] = readonly unknown[],
> = {
/** Calls to execute. */
calls: Calls<Narrow<calls>>
}
export type GetExecuteErrorReturnType =
| FunctionSelectorNotRecognizedErrorType
| GetContractErrorReturnType
export function getExecuteError<const calls extends readonly unknown[]>(
e: BaseError,
parameters: GetExecuteErrorParameters<calls>,
): GetExecuteErrorReturnType {
const error = e.walk((e) => 'data' in (e as Error)) as
| (BaseError & { data?: Hex | undefined })
| undefined
if (!error?.data) return e as never
if (
error.data ===
AbiError.getSelector(AbiError.from('error FnSelectorNotRecognized()'))
)
return new FunctionSelectorNotRecognizedError() as never
let matched: Call | null = null
for (const c of parameters.calls) {
const call = c as Call
if (!call.abi) continue
try {
const matches = Boolean(
decodeErrorResult({
abi: call.abi,
data: error.data!,
}),
)
if (!matches) continue
matched = call
} catch {}
}
if (matched)
return getContractError(error as BaseError, {
abi: matched.abi as Abi,
address: matched.to,
args: matched.args,
functionName: matched.functionName,
})
return e as never
}