Over the past seven days, an AI-agent vault on Arbitrum shed 41% of its deposits. No exploit transaction. No flash loan. No reentrancy. No governance capture. The vault executed 6,128 rebalances, each one small enough to slip under the operator's alert thresholds, each one individually defensible, each one collectively ruinous.
I spent two days replaying the logs. The model was not the problem. Its outputs, read as English, were reasonable. The signature scheme was correct โ EIP-712, domain-separated, nonce-incremented. The verifier contract never reverted once. Every guard the team had written was satisfied.
The failure lived in the space between what the model said and what the verifier agreed to hear. That space has a name in every audit report I have written since 2018: the parsing boundary. It is where I have found more critical findings than in any oracle, any bridge, any lending market. In AI-agent protocols, it is currently almost entirely unexamined.
The trust boundary is not where the team thinks it is
Here is the standard architecture. An off-chain process โ usually an LLM, sometimes a small ensemble โ reads market state and proposes an action. A signer, often a hot EOA or a TEE-backed enclave, wraps that proposal in a typed struct and signs it. An on-chain verifier contract checks the envelope: schema conformance, signature validity, nonce monotonicity, expiry window, target whitelist, slippage ceiling. If everything passes, an executor calls the router.
That is the whole design. Everything upstream โ the prompt, the context window, the model weights, the inference host โ is unverifiable by the chain. The chain sees a signed struct and nothing else. The security of an agent protocol is therefore determined entirely by the narrowness of the struct it accepts. Every field the verifier does not constrain becomes an implicit grant of authority.
Most teams I have reviewed understand this in principle. Fewer understand it operationally. I audited one such protocol in early 2026 โ an agent-driven perps router โ and the finding that mattered was not in the LLM orchestration. It was a single require statement.
Anatomy of a verifier that passed every test
The validateIntent function looked like this, paraphrased and stripped of naming:
function validateIntent(Intent calldata i, bytes calldata sig) external {
require(validSigner[i.signer], "bad signer");
require(i.nonce == nonces[i.signer]++, "bad nonce");
require(block.timestamp <= i.deadline, "expired");
require(whitelisted[i.target], "bad target");
require(i.minOut >= quotes[i.asset] * (10000 - maxSlip) / 10000, "slip");
require(verifyTyped(i, sig), "bad sig");
}
Six checks. Read them as an auditor and count what is missing.
The nonce increments before signature verification, which is a griefing vector but not the real issue. The deadline is validated but never bounded โ a signer can emit an intent with a deadline of type(uint256).max and it will remain valid forever. The target is whitelisted, but the calldata selector is not; whitelisting an address is not whitelisting a behavior. The slippage check compares against a stored quote, not against a live oracle read, so the quote is itself an input the signer controls.
Complexity is the bug; clarity is the patch. Every one of these gaps is a door left unlatched, and in an agent protocol those doors are opened by a process whose outputs are non-deterministic by construction.
I rebuilt the vault locally and ran the same intents. Then I started feeding it adversarial prompts.
Four hundred prompts, seventeen bypasses
The fuzzing framework I used is not sophisticated. It does not need to be. I generated 400 adversarial inputs across six categories and measured how many produced an intent that passed validateIntent while contradicting operator intent.
Instruction injection through on-chain data. An ERC-20 name() field is attacker-controlled. So is every symbol(), every token URI, every NFT description. If the LLM's context window includes any of these strings โ and agent protocols routinely pipe token metadata into the prompt for asset identification โ an attacker can write directives into a contract that the model will read as context. The model does not need to be jailbroken. It needs to be consistent.
Numeric ambiguity. "Amount: 1.5" is meaningless without a unit. The model returns a number; the verifier applies a scale factor. If that scale factor is derived from the token's decimals() rather than from a typed field, and the token is rebasing or upgradeable, the multiplier can shift between the quote and the execution.
Unit and exponent confusion. This one produced the largest single loss in my test set. A Pyth price feed for the same pair returns 8 decimals; the protocol's internal accounting is 18. The verifier scaled by 1e18 unconditionally.
Symbol collision. Two assets named USDC on the same chain โ one bridged, one native. The verifier resolved by symbol string, not by contract address. The model had no way to disambiguate because the prompt contained only the symbol.
JSON truncation and trailing garbage. The signer's serialization pipeline truncated a long reasoning field mid-string. The verifier parsed the struct with a lenient decoder. The action executed with a partially populated field defaulting to zero โ and zero, in a minOut context, is a full slippage grant.
Confidence laundering. The model emits a confidence field. The verifier does not check it. Downstream risk logic reads it and scales position size. An adversary who can push the model toward high stated confidence โ trivial with a well-formed context โ scales the position without touching any authorization path.
Seventeen prompts out of 400 produced a verifier-passing intent that contradicted the operator's stated policy. That is a 4.25% bypass rate against a contract with no bugs.
The bytecode never lies, only the intent does. The contract did exactly what it was written to do. The intent was simply narrower than the world.
The oracle is not stale. It is latched.
Now the part that actually drained the vault.
Every agent protocol has a freshness check. It looks like this:
require(block.timestamp - price.publishTime <= MAX_AGE, "stale");
MAX_AGE is typically 3600 seconds. On a major pair with a 0.5% deviation threshold and a 24-hour heartbeat, that is fine. On a thin pair, it is a loaded gun.
In a sideways market โ which is what we have had for months โ the deviation threshold rarely trips. The aggregator does not push a new price because the price has not moved enough to justify the gas. So the feed ticks on heartbeat alone, and the last published price can be 55 minutes old while still passing the MAX_AGE check.
The price is not stale in the code's sense. It is economically dead. For a $12M vault, a 3.1% deviation from fair value is $372,000. That is not a rounding error; that is the entire risk budget for a quarter.
The vault I replayed did not blow up on a single bad price. It blew up on 214 consecutive executions against a feed that was latched โ frozen at a value the aggregator had not bothered to update because the deviation threshold, the very mechanism designed to protect against manipulation, never fired in a low-volatility regime.
This is the configuration nobody backtests. Historical runs happen on volatile data, where the deviation threshold fires constantly and prices stay fresh. Production runs in chop, where the threshold sleeps. The security assumption embedded in MAX_AGE is that staleness and deviation are correlated. They are not. They are independent failure modes, and only one of them is checked.
Every edge case is a door left unlatched โ and the chop regime is the edge case that stays open longest.
What the signer key actually authorizes
In every agent protocol I have reviewed, the verifier maintains a static whitelist of signer addresses. Adding a signer requires a governance transaction. Rotating one requires a governance transaction. Revoking one โ you can guess โ requires a governance transaction.
That is acceptable on a seven-day timelock in a calm market. It is not acceptable when the signer is a hot key held by an inference service that gets compromised at 03:00 on a Saturday. The gap between compromise and revocation is the entire loss window. In the Arbitrum vault, the revocation path was a 48-hour timelock. Forty-eight hours at 6,000 intents per week is roughly 1,700 signed actions executed by an authority the team had already decided to remove.
TEE-based signers help but do not solve it. An attestation proves the enclave is running the expected measurement at the moment of attestation; it says nothing about the prompt that entered it. I have watched attestation reports quoted in pitch decks as though they were a security property of the whole system. They are a property of one component.
The mitigation is boring, which is why nobody ships it: session keys with per-epoch spend limits enforced on-chain, independent of the verifier's whitelist. A signer that can move at most $50,000 per hour is a signer whose compromise is survivable. A signer with unbounded authority and a 48-hour kill switch is a single point of failure wearing a governance costume.
Regulation will arrive as a schema, not a statute
In 2024 I spent three months mapping a Layer 2 consensus mechanism against the emerging MiCA framework. The exercise taught me something about how this industry will actually be regulated: not through prose, but through technical annexes.
The pattern is already visible. Transaction finality proofs, oracle freshness attestations, key custody documentation, incident reporting windows measured in block time rather than business days. Regulators do not want to read your Solidity. They want a machine-checkable artifact asserting that your price feed was fresh within a defined tolerance at the moment of execution โ produced by the system, not by your legal team.
Agent protocols are uniquely exposed to this. A conventional DeFi protocol can argue that its execution is deterministic and its parameters are public. An agent protocol cannot. Its execution is a function of a model whose weights are private and whose outputs are stochastic. The only defensible position is to make the envelope auditable: typed intents, bounded authorities, logged oracle state, and a verifier whose accepted language is small enough to be described in a paragraph.
The remediation path is short and unglamorous. Replace string-based asset resolution with address-based resolution everywhere โ the model should never see a symbol, only a checksummed address and a chain ID. Make the oracle freshness check regime-aware, a function of recent realized volatility and observed push frequency rather than a constant. Type the intent fully, with unit-bearing fields, explicit decimal scale, bounded deadlines, and calldata selectors constrained alongside target addresses. A verifier that accepts a generic bytes payload has delegated its authority to whoever produces that payload.
Security is not a feature, it is the foundation โ and in agent systems, the foundation is the struct.
The DA layer is solving the wrong problem
I want to address where the capital is going, because it is going to the wrong layer.
The dominant pitch in agent infrastructure right now is data availability for reasoning traces. Publish the model's chain of thought, make it verifiable, make it cheap. Several teams have raised on this framing. The technical argument is coherent; the economic argument is not.
Do the arithmetic. A mid-sized agent vault executing 6,000 intents per week, each with a 2 KB signed payload and a 4 KB reasoning trace, produces roughly 150 MB per month. That is a rounding error against any general-purpose DA layer. You do not need a dedicated data availability solution to publish 150 MB. You need a blob.
The reasoning trace is not the attack surface. The action is. An immutable record of a model's reasoning is forensically useful โ I would like every agent protocol to have it โ but it prevents nothing. It is a post-mortem tool sold as a prophylactic. The teams spending on DA are not spending on the verifier, and the verifier is where the 4.25% lives.
There is a second layer of theater here. Institutional agent vaults increasingly gate deposits behind KYC, on the theory that identity-gating the depositor constrains the agent. It does not. The authority in these systems is the signer key, and the signer key is hot. A single verified depositor can route unlimited intent through a permissioned wrapper; the constraint binds the honest user who funds a wallet in their own name and never touches the delegated path. The market prices hope; the auditor prices risk โ and here, the compliance cost is paid entirely by the users who least needed monitoring.
Takeaway
The next wave of agent exploits will not be prompt injections that make headlines. They will be schema-level failures: a scale factor applied to the wrong exponent, a symbol resolved to the wrong address, a freshness constant that was correct in a volatile market and lethal in a flat one. None of these will look like a hack. All of them will clear the verifier.
I expect the first serious regulatory action in this space to target the absence of a formal intent specification, not the model behind it. Teams that can hand a regulator a typed schema and a freshness attestation will pass review. Teams that hand over a prompt template will not. The question worth asking is no longer whether your model is aligned. It is whether your struct is narrow enough to fail safely.