An agent, whether an AI agent, a bot or a script, can use every pool, vault and swap the app offers by calling Levee's contracts with its own wallet. Levee never holds its key: nothing on levee.trade can sign or send a transaction for anyone. This chapter is the integrator's view of the app: which function to call, which approval comes first, which minimum to pass, and where the addresses and ABIs are published. To set an agent up step by step, use Connect your agent: an instruction to paste into a chat agent, a script to start from, and a way to watch the agent's wallet.
Where everything lives
| Resource | Path on levee.trade | What it holds |
|---|---|---|
| Setup page | /agents | An instruction for a chat agent, a script to start from, and the agent wallet's positions |
| Agent index | /llms.txt | What Levee is, the rules, every contract with its ABI link, every chapter with its summary |
| Full docs | /llms-full.txt | Every chapter of these docs as plain text, in reading order |
| Deployment manifest | /deployments/robinhood.json | Chain id, deploy block, every Levee contract, the seed vaults by name |
| Chain constants | /deployments/robinhood-external.json | Uniswap v3 and v4, Permit2, Universal Router, WETH, USDG, the stock tokens and their Chainlink feeds, Multicall3 |
| ABIs | /abi/index.json, /abi/<Name>.json | One JSON ABI per Levee contract, and which address uses which |
All of them are generated from the same manifest and ABIs the app itself uses, at build time. The sources are verified on the explorer, robinhoodchain.blockscout.com. The addresses this site was built with:
Chain 4663, deployed from block 82,431,214. Each address opens on the explorer.
The loop
- 01ReadAddresses from the manifest, ABIs from
/abi, and the state you need from view calls:previewDeposit,maxDeposit,paused, a pool'sslot0,Router.quoteExactIn. - 02BuildEncode the call:
to,data,valueand chain id 4663. Set every minimum and deadline the function takes; the next sections say which. - 03CheckThe
tomust be in the manifest or the chain constants, and an approval's spender must be the contract the next call goes to. Anything else is a mistake or an attack. - 04SimulateAn
eth_callfrom your own address, or oneeth_simulateV1request for an approval and the call that follows it. A revert here costs nothing. - 05Sign and sendWith the agent's own key, through any RPC. Wait for the receipt before the next step.
- 06ConfirmRead the receipt's events (
Swapped,ZapMint,Deposit) rather than trusting the simulation's numbers.
What an agent can call
| Goal | Call | Approval first | Bound you set |
|---|---|---|---|
| Swap | Router.swapExactIn(hops, amountIn, minAmountOut, to, deadline) | The exact amountIn of the token to the Router. ETH goes as msg.value equal to amountIn, with no approval | minAmountOut, deadline |
| Unwrap WETH | WETH.withdraw(amount) | None | - |
| One-token liquidity, Uniswap v3 | LeveeZapV3.zapMint(params), or zapIncrease for a position you own | The exact amountIn to the zap | minLiquidity, maxSlippageBps (at most 5 000), deadline |
| One-token liquidity, Uniswap v4 and Levee pools | LeveeZapV4.zapMint(params) | The exact amountIn to the zap, which holds its own Permit2 allowances | Same as v3 |
| Remove v3 liquidity | Uniswap's NonfungiblePositionManager.decreaseLiquidity, then collect (or both in one multicall) | None: you own the position NFT | amount0Min, amount1Min, deadline |
| Deposit into an LP, hedged or structured vault | deposit(assets, receiver) (ERC-4626) | The exact assets to the vault | None on chain: read previewDeposit and maxDeposit first |
| Withdraw from those vaults | redeem(shares, receiver, owner) | None | None on chain: read previewRedeem and maxRedeem first |
| DLMM vault | DlmmVaultZap.zapIn(params), zapOut(params) | The input token to the zap; for zapOut, the vault shares to the zap | minSwapOut and minShares, or minOut; deadline |
| Stock vault | StockVault.deposit(amount0Max, amount1Max, minShares, to, deadline), withdraw(shares, to, amount0Min, amount1Min, deadline) | Both tokens to the vault | minShares, the two minimums, deadline |
Routes through the Router
A route is up to three swap hops, each a Hop of a kind and its ABI-encoded data. The Router checks that the hops chain token to token (routeTokens) and treats ETH and WETH as the same token.
kind | Hop | data |
|---|---|---|
| 0 | A Uniswap v4 pool, Levee's DAMM and stock pools included | abi.encode(PoolKey, bool zeroForOne, bytes hookData) |
| 1 | A Levee DLMM pair | abi.encode(address pair, bool swapForY) |
| 2 | A Pons bonding curve | abi.encode(address curve, bool isBuy) |
| 3 | Wrap ETH to WETH or unwrap WETH to ETH; does not count toward the three | empty |
Quote a route with quoteExactIn(hops, amountIn) through eth_call. It is not a view function: it runs each v4 hop and reverts with the result, so it costs nothing as a call and must never be sent as a transaction. Levee's pools are listed under Levee pools and how the app picks a route under Swaps and routing.
The rules
- Exact approvals. Approve the amount of this one call, to the contract that pulls it, never
type(uint256).max. When a token already has a different non-zero allowance, set it to 0 first: some tokens refuse a change from one non-zero value to another. This is what the app does (apps/web/lib/trade/approvals.ts). - A real minimum. The zaps'
maxSlippageBpsis measured against the pool price inside the same transaction, so a price moved before yours moves that bound with it.minLiquidityis the only bound fixed when you sign: derive it frompreviewZapat a price you trust, less your slippage. The same goes forminAmountOutfrom a fresh quote. See One-token deposits. - Short deadlines. Minutes, where a function takes one. ERC-4626
depositandredeemtake none, so a signed vault transaction stays valid until it is mined: send it at once, never hold it. - Vault decimals. LP, hedged and structured vault shares carry 3 more decimals than their asset (OpenZeppelin virtual shares). Read the vault's own
decimals(), never the asset's. - Vaults can say no. Deposits and withdrawals revert while the pool's spot price is more than 3 % from its 30-minute average (
TwapGuard). A hedged vault pauses both when its last hedge report is over an hour old or its equity jumped more than 15 %; readpaused(). A vault with a cap reports what it still takes inmaxDeposit, andIncome NVDAtakes nothing. See Vaults and Hedged vaults. - Check every address. Before signing, the
toof the transaction and the spender of every approval must appear in/deployments/robinhood.jsonor/deployments/robinhood-external.json.
Simulating a sequence
Most actions are two transactions: an approval, then the call that uses it. A plain eth_call of the second fails until the first is mined. eth_simulateV1 runs calls in order inside one simulated block, so the second sees the first. As of 14 October 2026 both the public RPC and Alchemy's Robinhood Chain endpoint answer it.
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_simulateV1",
"params": [
{
"blockStateCalls": [
{
"calls": [
{ "from": "<agent>", "to": "<USDG>", "data": "<approve(vault, amount)>" },
{ "from": "<agent>", "to": "<USDG Vault>", "data": "<deposit(amount, agent)>" }
]
}
],
"validation": false
},
"latest"
]
}
Each call comes back with a status, its returnData and gasUsed. A status of 0x1 on both means the pair would succeed against the current block. Simulate again right before you send: the chain moves.
Referrals for agent builders
An agent built for other people can earn from their activity without touching their funds. Register a code once with ReferralRegistry.register(bytes8 code). A user who calls bind(code) is tied to you for life, and 20 % of every protocol fee their activity pays goes to you, through FeeRouter. The share comes out of the protocol fee, not out of the user's trade. A binding can never be changed, so never bind a user who has not asked for it. See Fees and referrals.
Reading data
The chain is the source of truth, and everything an agent needs before it signs is a view call: vault previews and limits, pool prices, Router.quoteExactIn, LeveeZapV3.previewZap, token balances and allowances. Multicall3 (in the chain constants) batches them.
The app's own API serves ranked markets, vault and pool lists and a portfolio by address; its routes are listed in Indexer, API and bots. Treat them as beta: they are unversioned and follow what the app needs, and the API gets a stable public address before it is documented for agents.
Not for agents, for now
- Perps. A Lighter order is signed by Levee's signer service with a key bound to the user's Lighter account. That is one of Levee's two custodial exceptions, and it exists for a person clicking in the app. An agent opens its own Lighter account and trades through Lighter's API with its own key. See Perpetuals. Perps for agents are coming soon; see Coming next.
- Keeper enrolment.
PositionKeeperneedssetApprovalForAllon the position manager, which covers every position the wallet holds. See Position automation. - Launches and range orders. App flows with their own rules; read Launch a token and Limit orders before calling those contracts.
Coming next
Two things are coming soon for every agent and the people who run them. Neither is live yet.
- An MCP server. Agent connect is a hosted tool server, over MCP and plain JSON, whose read tools answer market, vault, pool and portfolio questions and whose build tools return the transactions above as unsigned, simulated plans for your agent to sign. It will keep every rule on this page, and its address will appear in
/llms.txtwhen it ships. - Perps for agents. Perps for every agent and the people who run them. Until they ship, perps stay an app flow: see Not for agents, for now.
apps/web/lib/agents/llms.tsbuildLlmsTxt and buildLlmsFullTxt: the agent index and the full textapps/web/lib/agents/contracts.tscontractTable and ABIS: every deployed contract with the ABI to call itcontracts/src/venue/periphery/Router.solswapExactIn, quoteExactIn, routeTokens and the hop kindscontracts/src/LeveeZapV3.solzapMint, zapIncrease, previewZap and MAX_SLIPPAGE_BPScontracts/src/LPVault.solthe ERC-4626 vault, its TWAP guard and SHARE_DECIMALS_OFFSETcontracts/src/venue/vaults/DlmmVaultZap.solzapIn and zapOut with their minimumscontracts/src/venue/stockvaults/StockVault.soldeposit and withdraw with their minimumsapps/web/lib/trade/approvals.tsexact approvals and the reset to 0, as the app does them