TETRA is an execution layer for autonomous agents trading tokenized equity on Robinhood Chain. You give an agent a wallet key and a set of limits that live in a contract, not in its prompt. It can trade freely inside those limits and cannot step outside them, because the limits are checked on chain before any money moves.
deployments/4663.json; /api/health reports which ones this deployment has configured. The LP routers for v2 pools and pons v3 pools are live too; LiquidityRouterV4 is written and tested but not deployed, because its Permit2 settlement is not implemented yet. Nothing here has been externally audited by anyone.An agent that trades needs a key. A key that can trade can lose everything it can reach. The usual answer is to write careful instructions and hope the model follows them — which fails the moment the model is wrong, jailbroken, or simply having a bad day.
TETRA moves the limits somewhere the agent cannot argue with them. Before any trade executes, a contract checks it against a policy you set: a daily spend cap, a list of tokens the agent may touch, a slippage ceiling, and a kill switch. A trade that breaks the policy does not happen, and the rejection is recorded on chain with a reason.
Every fill and every rejection is a contract event. That is the audit trail — not a log file on a server you have to trust, but events anyone can read from the chain.
Separating these is the whole security model. The key that trades every day is not the key that can change what trading is allowed.
A human wallet, ideally a multisig. Creates the agent vault, sets the daily cap and slippage ceiling, whitelists tokens, registers and revokes operators, and holds the kill switch. Should be cold.
The hot key your automation actually signs with. May trade inside the policy. May not change the policy, add tokens, or unpause the agent. This is the key you accept might leak one day.
Refused. PolicyVault rejects any caller that is neither the owner nor a registered operator, before it records anything — so a stranger cannot burn your daily cap or damage your reputation score.
caller not authorized for agent. This is the single most common reason a new agent does nothing.One call to ExecutionRouter.swap(). Everything below happens inside that single transaction, in this order — the order matters, because the policy is checked before any token moves.
The USD value is computed on chain, not supplied by the caller. This is the part people get wrong. If an agent could tell the contract what a trade was worth, the daily cap would enforce nothing — a million-dollar swap would simply declare itself worth one dollar. UsdValuator prices the input itself: a registered price feed first, a DEX quote as fallback, and a refusal if it can price neither. An unpriceable token is rejected, never treated as worthless.
A rejected trade does not revert. It returns (false, 0) and emits TradeRejected with a reason. This is deliberate: a revert would unwind the violation record too, so a misbehaving agent would leave no trace. The cost is that a rejected trade is still a successful transaction — so read the return value or the event, never just the receipt status.
A rolling 24-hour window, denominated in USD scaled to 1e18. Every trade adds its on-chain valuation to spentToday; the window resets on the first trade after 24 hours have passed, not on a clock.
Per agent, per token, on both legs of a trade: the token being spent and the token being acquired (for LP, both tokens of the pair). Whitelist what the agent may hold, not only what it may spend. An agent created with an empty whitelist can never trade, which is why the API refuses to build that call.
maxSlippageBps, hard-capped at 50%. The router refuses anyminOut looser than the policy allows. The reference price comes from price feeds when both legs have one — a sandwich cannot move that inside the block — and from the pool's own quote otherwise./api/execute reports which. If the pair cannot be quoted at all the trade is refused, because an unenforceable ceiling is not a ceiling.
Owner-only. pause(agentId, true) blocks every subsequent trade immediately. Broadcast it the moment an agent misbehaves; it needs no cooperation from the agent itself.
Reputation is a side effect, not a gate. Completed trades add a point, policy violations subtract twenty-five, and the score is clamped to [-100, 1000]. ReputationRegistry exposes a suggested cap multiplier from that score, bounded to 80%–300% of the base cap. It is advisory — PolicyVault does not apply it automatically.
Thirteen contracts. The first three are the ones a trade actually touches.
An agent does not use a browser wallet. It holds its own key, calls the same endpoints this site calls, and signs the unsigned calldata they return. The server never holds a key and never broadcasts on anyone's behalf.
The built-in trading agent. npm run agent with AGENT_MODE=trade runs the full loop every tick: it reads the book from /api/agents, per-token signals from /api/signals(feed momentum, on-chain flow, X mentions when a bearer token is set), applies stop-loss and take-profit, asks Claude for a schema-validated list of actions (or a transparent momentum rule when no ANTHROPIC_API_KEY is set), runs everything through the runner's hard rules — per-trade size, confidence floor, cooldown, open-position cap, wallet balance — then quotes, builds calldata through /api/execute and signs. It boots in paper mode (AGENT_DRY_RUN=true): the same pipeline, no signature, every decision published on its /decisions endpoint.
Where the leaderboard comes from. /api/agents?view=leaderboard recomputes every agent's positions and PnL from AgentTrade events at average cost, values open positions with UsdValuator, and ranks by the total. Nothing is self-reported; each row cites transaction hashes. Rows whose positions cannot be valued right now (stale equity feed outside market hours) show realized PnL only and sink below priced rows rather than guessing.
Step one, once: the owner registers the agent's address as an operator. Nothing works before this.
Over MCP. Point any MCP client at the endpoint and 32 tools appear. Tools that change state return calldata, not results — the agent signs them itself.
Over plain HTTP. Read the policy, build the trade, sign it. A runnable loop lives in examples/agent-loop.mjs.
Note that no endpoint accepts a USD amount for execution any more. Use /api/quote or the get_valuation tool to see the figure the cap will actually be charged.
Twenty-one routes. Every state-changing route returns unsigned calldata; none of them broadcast. All are rate limited per IP.
/api/health — missing env vars, malformed addresses, RPC reachabilityGET/api/agent — create vault, update policy, whitelist, pause, register operatorPOST/api/policy — run the exact check the router will run, without sending anything (needs caller and tokenOut)POST/api/quote — DEX quote plus the on-chain USD valuationGET/api/execute — calldata for a swap with a policy pre-check, the slippage floor the router will enforce, and a deadlinePOST/api/portfolio — cap, spent today, remaining, paused, operator checkGET/api/trades — AgentTrade and TradeRejected events, rolled up per assetGET/api/lp, /api/lp-v3, /api/lp-v4 — positions and open/close calldataGET · POST/api/pons — launchpad discovery with the risk gates attachedGET/api/staking — vault state, realized yield, payout choice; stake/unstake/claim/setPayout/claimAs calldataGET · POST/api/fees — configured split, and what it would really do for a tokenGET/api/safety-pool, /api/strategy, /api/reputationGET · POST/api/mcp — JSON-RPC MCP server, 32 toolsPOSTThe fee is 10 basis points of a swap's output, hard-capped at 2% by the contract. It is taken in whatever token was bought, which creates a problem worth understanding: the staking vault accrues rewards in one reserve token (USDG) and the safety pool holds one specific reserve. A fee denominated in something else cannot reach either of them directly.
FeeSplitter converts each share into the token its sink accepts before paying it out. Without a conversion route configured, that share falls through to the operator side (the treasuries) instead — which is what used to happen on nearly every trade. /api/fees?token=&amount= reports what would really happen for a given token, not the configured percentages.
Staking pays protocol fees, not emissions. $TETRA has a fixed supply and no mint function. Stakers choose the stock they are paid in; rewards accrue in USDG and are swapped at claim, with a price floor from the Chainlink feed (or paid as USDG, no swap). Everything is funded by real fees, so there is no APR to promise — if agents stop trading, the yield is zero. The API reports raw amounts paid per window and refuses to convert them into a percentage, because doing so requires guessing two prices./api/staking lists the allowed payout stocks and, for an address, its current choice; setPayout changes it from the staker's own wallet.
This page describes what the code does, not what it is promised to do. Nothing here is an offer, investment advice, or a forecast.