hackquest logo

AgentVault

Everyone is building AI agents that can trade. Almost nobody is building the part that stops them. AgentVault is agent permission and risk infrastructure on Robinhood Chain. A Genesis Agent NFT owns an isolated smart account governed by a deterministic risk engine the agent cannot override — the AI proposes, code decides. Live on testnet: five verified contracts, 126 tests, and no admin key anywhere in the system.

视频

技术栈

Solidity
Foundry
Open Zeppelin
ClaudeCode
Robinhood Chain Testnet
Arbitrum Orbit/Nitro Stack
Blockscout

描述

AgentVault — the deterministic risk layer an AI agent cannot override

In July, Robinhood launched its own chain, and part of what shipped were Agentic Accounts — a way to wire AI models directly into trading infrastructure. That is where the whole space is heading, and it raises a question with no good answer today: when an agent can move money, what stops it?

Right now, mostly prompt engineering and hope. The model is instructed not to do anything catastrophic, and everyone proceeds as if instructions were constraints. They aren't. A model that misreads its context, gets prompt-injected through market data, or simply produces malformed output has nothing structural standing between it and the account.

Most approaches to this problem validate what the agent asks for. AgentVault changes what the agent is able to ask.

The agent's entire vocabulary is one data structure with five fields: an asset, a direction, a size, a maximum slippage, and the timestamp of the market data behind it. There is no field for a contract address to call, no field for calldata, no field for a recipient, no field for a token approval. An agent that wanted to drain an account could not encode the attempt — not because validation catches it, but because the type has nowhere to put it.

Everything that can be expressed then passes through a deterministic validator. Not a model. Code. The risk engine's validate function is a pure view function with no storage, no access control, no admin and no pause. Anyone may call it, because calling it changes nothing. It checks the holder's kill switch before it checks anything about the trade, then mandate limits, asset allowlist, position cap, slippage and data freshness — and it returns a named reason rather than a bare revert. A revert says something failed. A reason code says which rule stopped it, which is what makes a rejection legible, recordable and honest.

Three further design choices, each enforced rather than promised.

No contract in this system has an owner, an admin, or a role-gated function. Not one. The asset allowlist is fixed at deployment with no setter. There is no privileged key to lose, leak or subpoena.

Withdrawal has no pause check on any code path. The kill switch stops automation and can never stop a holder reaching their own funds. That asymmetry is the difference between a safety feature and a hostage situation.

Collectible rarity cannot touch financial risk. The risk engine depends on an interface that cannot see an NFT's archetype at all, so a rare agent structurally cannot receive a higher limit.

The demo is ninety seconds, running live against deployed contracts. A proposal inside every limit is approved. A proposal over the position cap is rejected by name — MaxPositionExceeded. The holder then flips the kill switch in one transaction, and the identical proposal from earlier, same asset and same size and same slippage, comes back rejected as AutomationPaused. Nothing about the trade changed. Only the holder's instruction did, and the agent has no path to that switch.

What is built: 126 tests passing from a clean clone, including four stateful-fuzz invariants that assert withdrawal stays available in every reachable state and that a deposit can never be reported as profit. Static analysis with Slither finds five items in project code, zero high and zero medium, each triaged in the repository. Five contracts are deployed and source-verified on Robinhood Chain testnet, chain ID 46630, which was measured rather than read from documentation after two sources disagreed.

Paxos USDG is on the risk engine's asset allowlist at 0x7E955252E15c84f5768B83c41a71F9eba181802F, so proposals denominated in USDG are validated against mandate limits. That address came from Paxos first-party documentation and was then confirmed on chain, because explorer search on this testnet returns more than fifty tokens claiming to be USDG, five of them named literally Paxos USDG. The allowlist has no setter, so sourcing it from search would have baked an impostor in permanently.

You can try the risk engine yourself at markantpacheco.github.io/agentvault. Build a proposal, send it to the deployed contract, and get back an approval or a named rejection. There is no wallet to connect and nothing to sign, because validate is a pure view function. Set the size to 1999 and watch MaxPositionExceeded come back from chain 46630.

Who this is for: agent developers who need permission scaffolding they didn't write themselves, and capital allocators who need a record of what an agent really did — including the losses and the trades that were refused. Today the only evidence a strategy works is a screenshot, and screenshots are free to fabricate. The record here is append-only, includes rejections, and travels with the NFT when it's sold.

Where this honestly is: early. No users and no real capital. The web page is a read-only window onto the risk engine, not a product — no accounts, no wallet connection, no write path, because nothing in this system executes a trade.

Not a fund. No custody. No advice on securities. No claim the system cannot lose money — it can.

The repository documents every major decision, its cost, and what would reverse it. The assumptions register separates verified from unverified from blocking, and includes a corrections log of assumptions that turned out wrong — including a test count this project reported, caught, and corrected downward before anyone asked. That discipline is the actual product: a performance record is only worth something if the project keeping it doesn't overstate things.

本次黑客松进展

CONTRACTS WRITTEN AND TESTED Archetype — permanent collectible identity, four types. GenesisAgent — an ERC-721 with a permissionless capped mint, where the archetype is assigned once and can never be changed. AccountRegistry — one isolated account per NFT, with principal and profit tracked as separate fields, lazy detection of NFT transfers, and a holder-controlled kill switch. Mandate — four risk mandates with prototype parameters, selected by the holder rather than determined by the NFT. RiskEngine — a pure-view deterministic validator that returns named rejection reasons instead of reverting. DEPLOYED AND SOURCE-VERIFIED ON ROBINHOOD CHAIN TESTNET GenesisAgent — 0x0EBdDD089f8203DD5cD1Bd1f148F75757285CF96 AccountRegistry — 0x0D0080582D317D2878A31b01D97614a3E918dC65 RiskEngine — 0x36df9096162b9f18d13574Fba930C9e2Ce6cc4a4 TestAsset AVTA — 0x87CEd5dc138F825B3F42924A8Bb6E15ae5EC9A7d TestAsset AVTB — 0x9D97ebc6A395aaf981B26d952A4e22604eAFEc75 All five are source-verified. The full demo arc has run on chain, including a real kill-switch transaction. An earlier RiskEngine is still deployed and verified and is marked superseded in the deployment record, with a date and a reason, rather than deleted. Extending an immutable allowlist means redeploying — that is the cost of having no admin key, and it was paid rather than designed around. PAXOS USDG, AT AN ADDRESS ESTABLISHED THE HARD WAY The risk engine's allowlist is fixed at deployment and includes USDG at 0x7E955252E15c84f5768B83c41a71F9eba181802F. Explorer search on this testnet returns more than fifty tokens claiming that ticker, five of them named literally Paxos USDG, and all five were confirmed on chain as real deployed ERC-20s wearing the name. The genuine contract is named plainly Global Dollar. The address was taken from Paxos first-party documentation and then verified on chain. Because the allowlist has no setter, sourcing it from explorer search would have been permanent. This is written up as security finding S1. VERIFIED RATHER THAN ASSUMED The chain ID was measured directly rather than read from documentation, closing an open question where a third-party source disagreed with the official docs. Blockscout's verifier endpoint was established by probing its API, not by assuming the conventional URL. Verification status was confirmed independently — the Foundry CLI reported one contract as already verified while the explorer still had it unverified. PROPERTIES ENFORCED, NOT JUST CLAIMED No contract in this system has an owner, an admin, or a role-gated function. Withdrawal has no pause check on any code path. The kill switch stops automation and can never stop the holder reaching their own funds. Collectible rarity cannot affect risk limits. The risk engine depends on an interface that cannot see an archetype at all, proven by a test suite that mints all four archetypes and asserts identical verdicts. Two mandate parameters are specified but not yet enforceable. They carry notes in the code and are recorded as pending in the decision log rather than presented as working. SECURITY ANALYSIS PUBLISHED Static analysis with Slither 0.11.6 found five items in project code — zero high, zero medium. Each is listed by detector name with a triage verdict: three accepted with reasoning, two false positives explained. None was suppressed with an inline disable comment. The unfiltered run reports twenty-five findings, twenty of which are inside third-party libraries, and both numbers are published so the exclusion is visible rather than implied. Two findings are real improvements that were deliberately not applied, because the affected contracts are already deployed and source-verified and editing the source would leave the explorer's verified code no longer matching the repository. Both are recorded as carried forward to the next redeployment. STATEFUL INVARIANT TESTS Four invariants, each one a claim the project makes publicly, driven by a handler contract that randomises sequences of minting, deposits, withdrawals, pauses, NFT transfers and synchronisation. Withdrawal liveness — in every reachable state, including paused, mid-transfer and unsynchronised, the current holder can withdraw in full. A deposit is never profit — profit and loss equals balance minus net principal, checked against independent accounting in the handler rather than against the contract's own arithmetic. Paused implies never approved — across every reachable state, not just the one a unit test constructs. Archetype cannot influence risk — parallel accounts differing only in collectible archetype return identical verdicts and identical reasons. Sixteen thousand three hundred and eighty-four calls per run with zero reverts, verified across eight consecutive runs and three explicit fuzz seeds, all recorded. THE TRAP IN INVARIANT TESTING An invariant suite whose handler calls all revert passes perfectly while exploring nothing. So the handler counts successful state mutations rather than attempted calls, and a separate ordinary test asserts those counts are non-trivial. The synchronisation function firing on its own is the result that matters: it can only fire when an NFT transfer is genuinely pending, so a non-zero count proves the stale-transfer window is actually being entered rather than assumed. That coverage assertion cannot live in Foundry's afterInvariant hook. When any invariant fails, Foundry shrinks toward the shortest failing sequence, and a coverage assertion is trivially failable by a one-call sequence — so the shrinker drives straight at it and reports a nonsense counterexample naming an invariant that never broke. Found the hard way, fixed by splitting reporting from assertion, and written into the repository's gotchas file with the full symptom. A CORRECTION, ON THE RECORD The suite reports 126 tests. Earlier in the Buildathon it also reported 126, and that number was not real. One test wrote an environment variable to check a deploy-script guard. Foundry runs test contracts in parallel, and a second test contract wrote the same variable, so the two raced and the suite passed intermittently. Five consecutive local runs produced three failures. The 126 was simply the run that had been looked at. It was caught by running the suite repeatedly from a clean clone rather than once, then fixed by removing the race. Four guard tests became two and no assertion was lost. The honest count after that fix was 124. Adding the invariant suite brought it back to 126, by coincidence, from a different set of tests. That coincidence was reconciled rather than waved through: the eight pre-existing suites still collect exactly 124, so no test quietly stopped being collected, which is the failure mode that looks identical to a clean pass. This is recorded rather than quietly corrected because the product is a performance record that includes losses and refused trades. A project that rounds its own test count up has no business claiming to keep an honest one.

融资状态

Self-funded. No outside capital raised, no round in progress. Total spend to date is effectively zero — free tooling and testnet gas. Near-term funding plan is ecosystem grants rather than equity or token sales. A Genesis Agent mint is deliberately deferred until the product has real users, and would be sized to actual demand rather than to a funding target.
队长
MMark Pacheco
项目链接
赛道
DeFiAIInfra