Skip to content

Instantly share code, notes, and snippets.

@bkpleb
Created July 26, 2025 16:38
Show Gist options
  • Select an option

  • Save bkpleb/071979f0b2a19089f8f4acf40c4368cc to your computer and use it in GitHub Desktop.

Select an option

Save bkpleb/071979f0b2a19089f8f4acf40c4368cc to your computer and use it in GitHub Desktop.
decode Alchemy calldata
import { parseAbi, decodeFunctionData } from "viem";
export interface Execution {
target: string;
value: bigint;
callData: `0x${string}`;
}
// ABI covering all four entry-points
const accountAbi = parseAbi([
"function execute(address target,uint256 value,bytes data) payable returns (bytes)",
"function executeBatch((address target,uint256 value,bytes data)[] calls) payable returns (bytes[])",
"function executeWithRuntimeValidation(bytes data, bytes authorization) payable returns (bytes)",
// note: paymasterAndData is bytes, not address
"function executeUserOp((address sender,uint256 nonce,bytes initCode,bytes callData,uint256 callGasLimit,uint256 verificationGasLimit,uint256 preVerificationGas,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,bytes paymasterAndData,bytes signature) userOp,bytes32 aggregator) payable"
]);
export function decodeAlchemyAccountCall(data: `0x${string}`): Execution[] {
const { functionName, args } = decodeFunctionData({
abi: accountAbi,
data,
});
if (!args) {
throw new Error(`Failed to decode function data for ${functionName}`);
}
switch (functionName) {
case "execute": {
const [target, value, callData] = args as [
string,
bigint,
`0x${string}`
];
return [{ target, value, callData }];
}
case "executeBatch": {
const raw = args[0] as Array<{
target: string;
value: bigint;
data: `0x${string}`;
}>;
return raw.map(({ target, value, data }) => ({
target,
value,
callData: data,
}));
}
case "executeWithRuntimeValidation": {
// args[0] is the inner calldata for execute/executeBatch
const innerData = args[0] as `0x${string}`;
// recurse to decode that exactly like a normal call
return decodeAlchemyAccountCall(innerData);
}
case "executeUserOp": {
// args[0] is the PackedUserOperation struct
const userOp = args[0] as Record<string, any>;
const innerData = userOp.callData as `0x${string}`;
return decodeAlchemyAccountCall(innerData);
}
default:
throw new Error(`Unsupported function ${functionName}`);
}
}
const data = "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000040b35800bb3e536aee3dc5dbd46d8f0a39c4dffc000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000039008584ef5a94fba2d6d27669429ab47c1fc8e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
// Decode & print
const calls = decodeAlchemyAccountCall(data as `0x${string}`);
console.log(`Decoded ${calls.length} call(s):`);
calls.forEach((c, i) => {
console.log(`\nCall #${i + 1}`);
console.log(" target: ", c.target);
console.log(" value: ", c.value.toString());
console.log(" callData:", c.callData);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment