Justin Cheng and Nathan Liang
AI agents in crypto keep running into the same wall. You want an agent to manage your DeFi transactions and portfolio (rebalancing, collecting yield, executing payments, etc.) but in order to do so, you have to hand over custody of your funds. We’ve seen that agents are fundamentally prone to compromise, hallucination, and manipulation, so we simply cannot blindly trust that they will execute as intended. The alternative – closely monitoring agents and approving each and every transaction – eliminates the convenience that agents offer us, defeating the entire purpose.
ERC-8004 attempts to address this issue through a reputation-based framework. Agents register on what is essentially an on-chain agent marketplace, where users can leave trackable reviews. If an agent has a solid track record and economic stake, you can trust it more. This helps, but it doesn’t solve the issue entirely; even the most reputable agent is still prone to exploitation and occasional hallucination. What users need is an absolute guarantee on agent behaviour. 8004 takes an extra step in this direction through a validation registry, where agents can request external validation, but this doesn’t place any hard, technical constraints on the agents’ ultimate execution.
ERC-8150 is an answer to this problem. The core idea is that the user reviews and signs a batch of transactions upfront, and the agent must then submit a zero-knowledge proof that its actual calldata matches what the user initially signed, before any execution happens. If the proof is faulty, the entire transaction bundle reverts.
ERC-8150 specifies three components:
A structured object containing chainId, nonce, expiry, payer, and an array of Action structs (token address, recipient, amount). The user ABI-encodes this, hashes it, and signs the commitment. EIP-712 typed data signing is recommended here, so wallets can render it readably.
Proves that the agent's multicall data was correctly derived from the signed bundle. Concretely: the commitment hash matches, calldata encodes the correct transferFrom selectors with the right parameters, and chain/payer/nonce/expiry are consistent. Groth16 is recommended for simplicity and efficiency.
For example, consider Alice's intent bundle: pay merchants A, B, C - 10, 150, and 35 USDC respectively. The circuit checks that the agent's submitted calldata contains exactly:
calldata[] = [
transferFrom(0xAlice, 0xMerchantA, 10_000000),
transferFrom(0xAlice, 0xMerchantB, 150_000000),
transferFrom(0xAlice, 0xMerchantC, 35_000000)
]
and that keccak256(abi.encode(calls[])) matches the multicallDataHash public input. Then it checks that keccak256(intentBundle) matches the commitment hash.
Finally, the parameter checks are:
intentBundle.chainId == 1; // ethereum's chain ID = 1
intentBundle.payer == 0xAlice; // Alice's address
intentBundle.nonce == 12345; // example nonce
block.timestamp < 0x63351330; // timestamp
Performs the actual execution on behalf of the agent. Accepts (proof, signature, publicInputs, calls[]), verifies the proof, and validates the signature, nonce and expiry. Only if all checks pass, the contract executes all transfers atomically via multicall. Any failed transfer reverts the entire batch.
Critically, the flow is non-custodial; instead of placing control of funds in the hands of the agent, users approve() the Agent Wallet contract, and the contract itself makes each of the calls found in the calldata bundle.
The public inputs to the circuit include commitment (the hash of the intent bundle the user signed) and multicallDataHash (the hash of the calldata the agent wants to execute), as well as chainId, signerAddress, nonce, expiry. The original IntentBundle serves as the private input. The circuit checks that both hashes are valid and compares the remaining inputs with those found in the IntentBundle, constraining it to the fixed values from the public inputs.