AI treasury agent that trades on Uniswap and asks a World ID-verified human before big moves
AXIS is an autonomous treasury agent with a human in the loop for the moves that matter.
AI agents are starting to hold money, which forces an awkward choice: either a human signs every transaction (so the agent isn't really autonomous), or the agent can do anything (so one bug or bad decision can drain the wallet). AXIS puts a policy between the two.
The agent manages a treasury of ETH and USDC on Sepolia and keeps it near a 50/50 target. Every cycle it values the portfolio with a live Uniswap quote. When the allocation drifts more than 10% from target, it proposes a rebalancing trade, and a policy engine sorts the trade into one of three tiers:
Before any of that, the agent is switched off. The owner turns it on by connecting Uniswap Wallet and signing a gasless EIP-712 "mandate" that sets a max per trade, a daily limit and an expiry. The policy enforces those limits, and the owner can revoke the mandate at any time with another signature from the same wallet, which pauses the agent immediately.
A World ID approval is bound to a single proposal and can only be used once. It has to be recent (≤ 120s), and it must come from the same verified human who owns the treasury, matched on World ID's pairwise subject. If the user cancels, World ID returns an error, the approval window expires, or a different person verifies, the proposal is rejected and no transaction is sent.
The dashboard shows treasury balances and allocation, the active delegation, trades waiting for approval, charts of allocation (with the target band and rebalance markers) and treasury value over time, a rebalance history with allocation before → after, who authorized each trade (the agent alone or a World ID-verified human) and Etherscan links, plus a live activity log of every decision and its reason.
Backend: TypeScript on Node with Express, viem for chain access, and jose for JWT verification. The agent loop, policy engine, delegation, World ID and Uniswap client are small separate modules. State is kept in memory to keep the hackathon build simple.
Uniswap: all pricing and execution go through the Uniswap Trading API on Sepolia. The agent calls /quote both to value the treasury (a small probe quote gives the USDC/ETH price) and to size each trade. For native ETH input it sends the swap straight away. For USDC input it checks the Permit2 allowance, sends a one-time approve if needed, signs the quote's permitData (EIP-712 PermitSingle) with the agent key, calls /swap and broadcasts the transaction. Without extra work, the API routed our trades through both v3 and v4 pools on Sepolia, and we show the route in the UI.
World ID for Agents: we integrated the sandbox as a standard OpenID Connect provider. Each medium trade starts its own authorization-code flow with PKCE (S256), a random state bound to the proposal id, a nonce, prompt=login and max_age=120 to force a fresh verification. On the callback, the backend exchanges the code with the client secret (client_secret_basic), verifies the ID token signature against World's JWKS, and checks the issuer, audience, nonce and auth_time before it lets the agent execute. The first verified pairwise sub becomes the treasury owner, and later approvals must match it. The client secret never reaches the browser.
Delegation with Uniswap Wallet: the frontend uses EIP-6963 to find the Uniswap Wallet extension even when other wallets are installed, and asks it to sign an EIP-712 Mandate { owner, agent, maxTradeWei, dailyLimitWei, expiry, nonce } with eth_signTypedData_v4. The backend verifies it with viem's publicClient.verifyTypedData. That covers plain EOAs, and should also cover a Uniswap smart wallet (Calibur via EIP-7702, checked through ERC-1271), though we only tested it with a plain EOA. The nonce is single-use, and revoking requires a personal_sign from the same owner.
Frontend: plain HTML, CSS and JavaScript served by the backend (no build step), with Chart.js for the allocation and value charts. It supports light and dark themes and works on phone-width screens.
Hacky bits worth mentioning:

