UNPKG

ox

Version:

Ethereum Standard Library

198 lines (182 loc) 5.03 kB
import * as AbiFunction from '../core/AbiFunction.js' import * as Hex from '../core/Hex.js' import { AbiParameters } from '../index.js' import * as Calls from './Calls.js' export type Batch = { calls: readonly Call[] opData?: Hex.Hex | undefined } export type Call = Calls.Call /** Cached ABI parameters for the encoded `bytes[]` payload of batch-of-batches. */ const batchesParameters = /*#__PURE__*/ AbiParameters.from('bytes[]') export const abiFunction = { type: 'function', name: 'execute', inputs: [ { name: 'mode', type: 'bytes32', internalType: 'bytes32', }, { name: 'executionData', type: 'bytes', internalType: 'bytes', }, ], outputs: [], stateMutability: 'payable', } satisfies AbiFunction.AbiFunction export const mode = { default: '0x0100000000000000000000000000000000000000000000000000000000000000', opData: '0x0100000000007821000100000000000000000000000000000000000000000000', batchOfBatches: '0x0100000000007821000200000000000000000000000000000000000000000000', } as const /** * Decodes calls from ERC-7821 `execute` function data. * * @example * ```ts twoslash * import { Execute } from 'ox/erc7821' * * const { calls } = Execute.decodeData('0x...') * ``` * * @param data - The encoded data. * @returns The decoded calls and optional opData. */ export function decodeData(data: Hex.Hex): decodeData.ReturnType { const [m, executionData] = AbiFunction.decodeData(abiFunction, data) as [ Hex.Hex, Hex.Hex, ] return Calls.decode(executionData, { opData: m !== mode.default }) } export declare namespace decodeData { type ReturnType = { calls: Call[] opData?: Hex.Hex | undefined } } /** * Decodes batches from ERC-7821 `execute` function data in "batch of batches" mode. * * @example * ```ts twoslash * import { Execute } from 'ox/erc7821' * * const batches = Execute.decodeBatchOfBatchesData('0x...') * ``` * * @param data - The encoded data. * @returns The decoded batches. */ export function decodeBatchOfBatchesData( data: Hex.Hex, ): decodeBatchOfBatchesData.ReturnType { const [, executionData] = AbiFunction.decodeData(abiFunction, data) as [ Hex.Hex, Hex.Hex, ] const [encodedBatches] = AbiParameters.decode( batchesParameters, executionData, ) as readonly [Hex.Hex[]] return encodedBatches.map((encodedBatch) => { // Structurally detect whether the batch was encoded with opData by // inspecting the first ABI head word (the offset to the first dynamic // param). For a single dynamic param tuple `(Call[] calls)` the head // is one word, so the offset is 0x20. For a two dynamic param tuple // `(Call[] calls, bytes opData)` the head is two words, so the offset // is 0x40. const firstHead = Hex.toBigInt(Hex.slice(encodedBatch, 0, 32)) const withOpData = firstHead === 64n const decoded = Calls.decode(encodedBatch, { opData: withOpData }) if (withOpData && decoded.opData) return { calls: decoded.calls, opData: decoded.opData } return { calls: decoded.calls } }) } export declare namespace decodeBatchOfBatchesData { type ReturnType = Batch[] } /** * Encodes calls for the ERC-7821 `execute` function with "batch of batches" mode. * * @example * ```ts twoslash * import { Execute } from 'ox/erc7821' * * const data = Execute.encodeBatchOfBatchesData([ * { * calls: [ * { * data: '0xcafebabe', * to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', * value: 1n * } * ] * }, * { * calls: [ * { * data: '0xdeadbeef', * to: '0xcafebabecafebabecafebabecafebabecafebabe', * value: 2n * } * ], * opData: '0xcafebabe' * } * ]) * ``` * * @param calls - The calls to encode. * @param options - The options. * @returns The encoded data. */ export function encodeBatchOfBatchesData(batches: readonly Batch[]) { const b = AbiParameters.encode(batchesParameters, [ batches.map((b) => { const batch = b as Batch return Calls.encode(batch.calls, { opData: batch.opData, }) }), ]) return AbiFunction.encodeData(abiFunction, [mode.batchOfBatches, b]) } /** * Encodes calls for the ERC-7821 `execute` function. * * @example * ```ts twoslash * import { Execute } from 'ox/erc7821' * * const data = Execute.encodeData([ * { * data: '0xcafebabe', * to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', * value: 1n * } * ]) * ``` * * @param calls - The calls to encode. * @param options - The options. * @returns The encoded data. */ export function encodeData( calls: readonly Call[], options: encodeData.Options = {}, ) { const { opData } = options const c = Calls.encode(calls, { opData }) const m = opData ? mode.opData : mode.default return AbiFunction.encodeData(abiFunction, [m, c]) } export declare namespace encodeData { type Options = { opData?: Hex.Hex | undefined } }