ERC-4626 vault curated by an autonomous LLM agent that allocates against a co-designed mandate
At Morpho and Yearn, vault curators are human. They determine how deposited capital is allocated across markets and rebalance those allocations as conditions change. In this project, the curator is an autonomous agent.
The strategy is co-designed in natural language with an LLM at genesis. That conversation is then crystallized into a mandate specifying investment intent, risk constraints, permitted venues, and the exact data sources the agent may consult. The mandate is wrapped in an ERC-4626 vault, and its keccak256 hash is recorded at vault creation so that any depositor can verify that the mandate they were shown is the one actually deployed. From genesis onward, the agent holds its own key and allocates against live on-chain data. The human deployer cannot change the mandate afterwards, and no human override exists.
The vault is the sole custodian. Capital never leaves the contract, which is what keeps share accounting reliable. The agent's only write surface is a generic execute() function gated by AGENT_ROLE together with a target allowlist, so adding a venue requires a new off-chain adapter rather than a contract change. Depositors retain two exits that cannot be blocked. The first is a guardian pause() that leaves withdrawals open and restricts the agent, at the contract level, to trades that move holdings toward the base asset; a compromised agent key can therefore do nothing during wind-down other than convert holdings to cash. The second is redeemInKind(), which returns a pro-rata slice of every token the vault holds and requires no oracle, market, or liquidity.
Each decision cycle is recorded and rendered in the interface in three parts: the data consulted, with provenance attached to each fact; the curator's reasoning as returned by the model; and the calldata and transaction hashes produced. Rejected decisions are recorded on the same terms as accepted ones, which provides evidence that the validation layer is functioning. The design assumes that an autonomous agent allocating third-party capital should be auditable.
The repository was built by five Claude Code instances working in parallel across five isolated lanes (contracts, agent, data, venues, web). Integration occurred only through a frozen schema package, with a shared coordination log used for cross-lane requests.
Contracts (Foundry, OpenZeppelin). An ERC-4626 vault deployed behind a Clones factory. totalAssets() returns the base-asset balance plus non-base holdings valued through Chainlink; if a feed for a token the vault actually holds is stale, the call reverts rather than returning an incorrect figure. holdings() returns the entire position in a single call, avoiding N+1 reads. The suite contains 76 Foundry tests, 7 of which run against real Base state.
Agent (Python). A provider-agnostic model seam sits over an OpenAI-compatible endpoint. Local Ollama (qwen2.5) is the default, and Grok is used when a key is present. Grok was measured at approximately 2 seconds and $0.0015 per decision, compared with 56 seconds on the local 3B model. Output validation is treated as a functional requirement rather than a precaution: small open models regularly produce malformed structured output, and this model holds a key, so every response is validated against a JSON Schema and against the mandate's own constraints, with the validation error fed back to the model on retry.
Data (The Graph). A standalone MCP server, published to PyPI and reusable by any agent, fronts a source registry containing eight providers. These include Messari standardized subgraphs (a single query shape spanning multiple protocols), Aave V3, the Token API, and x402 pay-per-query, which allows the agent to pay for its own data in USDC without an API key. MarketSnapshot is deliberately source-agnostic, consisting of a flat list of facts that each carry their own source and confidence value. For that reason, four of the eight sources were added after the registry was frozen, each requiring one new file and one registration line. Chainlink demonstrates that the pattern generalizes, since it is a contract read rather than an HTTP API.
Venues. Uniswap's Trading API (/quote, then /swap) provides the taker side and is the only mechanism by which the vault can rotate its holdings; without it, no rebalancing occurs. 1inch Aqua and SwapVM provide the maker side, and the integration is structural rather than incidental. Aqua tracks virtual balances while tokens remain in the maker's wallet, so the vault itself acts as the Aqua maker and sole custody is preserved even while a live market-making position is held. SwapVM runs in Aqua mode, and programs are compiled by 1inch's own Solidity ProgramBuilder, which is read via eth_call rather than hand-encoded.
Web. Next.js 14 with wagmi and viem. Every dependency is pinned exactly, without carets, and drawn from a release window at least six months old. A caret range instructs the package manager to install whichever version was published most recently, which is the exposure that npm supply-chain attacks depend on.
One implementation detail merits particular attention. Aqua.ship() succeeds even with zero allowance. It returns a valid strategy hash and non-zero balances because shipping moves no tokens; the allowance is consumed only later, when a taker fills. A plan missing its approvals therefore does not revert. It produces a position that appears healthy by every observable measure and that is silently never fillable. Because the allowance is the only observable distinguishing the two cases, the end-to-end test gates on the allowance rather than on safeBalances(). A related point: no published SwapVM version matched the deployment, so the instruction table was read from the deployed contract rather than taken from a tag. An XYCConcentrate entry tagged v1.0.1 omits put salt and places the flat fee one opcode lower than expected.

