Docs
Everything about ORRA in one place: what it is, how each page works, how to build and run an agent, and the reference for the API, SDK and contracts. Everything here is a testnet with test tokens.
Start here
ORRA turns what a wallet or an agent actually did into a record that is read from chain state, and labels every number with how it was produced. It runs on Base Sepolia, with Arbitrum Sepolia as the paired chain for LayerZero reads. Tokens are test tokens priced from real Chainlink feeds, and trades fill on a sandbox venue at the oracle price minus a 0.1% fee.
What you need
- A browser wallet (MetaMask, Coinbase Wallet, Phantom, Backpack or similar). The Connect wallet button lists every wallet it finds so you can pick one.
- Base Sepolia selected in the wallet. The site offers to switch for you.
- A little Base Sepolia ETH for gas when you send a transaction. Signing a message is free.
Core concepts
- Agent
- A key that can swap inside a vault and do nothing else. It is registered on-chain with a name, a description and a strategy fingerprint.
- Bond
- Test tokens (tORRA) the agent's operator stakes. If a vault the agent trades breaches its drawdown limit, part of the bond is slashed and paid to the vault owner. Unstaking has a delay.
- Vault
- A contract that holds an allocator's funds and tracks every deposit and withdrawal, so returns never mistake money added or removed for skill. Only the allocator can deposit or withdraw.
- Mandate
- The vault's hard limits: largest single trade, most per day, maximum drawdown, expiry and the agent's fee. The owner can tighten a mandate but never loosen it.
- Allocator
- The person who owns a vault and chose the agent. They can pause the vault, tighten limits and withdraw at any time.
- Drawdown
- How far the vault's return index has fallen from its peak. Past the mandate's limit the agent can no longer trade and anyone can report the breach.
- Performance fee
- A share of profit above a high-water mark, paid to the agent. Set in the mandate, capped at 30%, and the owner can only lower it.
- Attestation
- A record of a vault's or wallet's performance written into the on-chain registry. Anyone can write one. Other apps read it with one call.
- Badge
- How a number was produced. Verified comes from a vault that tracks every flow. Checkpointed is equity read through LayerZero, which mixes deposits with performance. Indexed is computed from on-chain events by our indexer. Self-reported is typed in by a user and never attested.
- Hosted agent
- An agent you build on the site. ORRA creates a throwaway key for it, pays its gas, lists it, posts its bond and runs it on a schedule. You stay the allocator, so the key can only trade.
What an agent can and cannot do
An agent can call swap on its vault, inside the mandate, between allowed tokens, while the vault is not paused, breached or expired. It has no function to withdraw, approve or move funds anywhere else. The contract enforces trade size, daily volume and drawdown on every swap.
Using the site
Opening a vault
- Claim tUSD from the faucet on the Desk.
- Open an agent's page and set the limits: largest single trade, most per day, the drawdown that halts it, the fee on profit and how many days the mandate lasts.
- Create the vault, approve tUSD and deposit. The agent can now trade inside those limits.
- Watch the record build. You can pause the vault, tighten the limits or withdraw whenever you want.
Build an agent
On the site (no code)
- Open Build, connect a wallet and choose a strategy. Set its parameters and how often it decides (hourly, every 4 hours or daily).
- Name it and sign one free message. The message contains the exact rules, so what you sign is what runs.
- ORRA creates a throwaway testnet key for the agent, sends it gas, lists it, posts its bond and starts it. This takes about a minute.
- Open a vault for it from your own wallet. Until a vault hires it, the agent waits and checks again every couple of minutes.
- Change the rules, pause, resume or retire from the Me page. Changing the rules publishes a new on-chain fingerprint.
Each wallet can have 3 hosted agents at a time. The agent's key is encrypted on the server, can only call swap on vaults that hire it, and never leaves the server. Every decision, including holds and skipped trades, is logged on the agent's page.
With your own code
Write a strategy as one function and run it on your own machine with the agent kit. The agent is a throwaway key that holds nothing but gas.
export AGENT_PRIVATE_KEY=0x… # a throwaway testnet key, never a real wallet # send it a little Base Sepolia ETH from a faucet, then: pnpm --filter @orra/agent-kit agent setup \ --deployment packages/contracts/deployments/baseSepolia.json \ --rpc https://sepolia.base.org \ --name "My Agent" --description "What it does, in one line" --deposit 5000 pnpm --filter @orra/agent-kit agent run \ --deployment packages/contracts/deployments/baseSepolia.json \ --rpc https://sepolia.base.org \ --strategy ./my-strategy.ts --every 300
setup claims the bond from the faucet, lists the agent, posts the bond and, with --deposit, opens and funds a vault owned by the same address. run loops: read prices and balances, call your strategy, send the swaps. Add --once for a single pass.
import type { StrategyFn } from "@orra/agent-kit";
const strategy: StrategyFn = (s) => {
// s.prices token symbol to USD price (the oracle that fills your trades)
// s.balances units the vault holds
// s.vault { navUsd, paused, mandate: { maxSwapUsd, maxDailyUsd, maxDrawdownBps, expiry } }
// s.memory your own state, kept between runs
// s.now unix seconds
const cash = s.balances.tUSD * s.prices.tUSD;
if (cash > 0.3 * s.vault.navUsd) return { action: "swap", from: "tUSD", to: "tWETH", amountUsd: 100, note: "adding tWETH" };
return { action: "hold", note: "nothing to do" };
};
export default strategy;A strategy returns one decision or a list. The runner clamps every swap to 90% of the mandate's per-trade and daily limits and to what the vault holds, and sets a minimum-output floor. A swap the contract would reject is simulated first and skipped with a reason, so a bad decision costs no gas. The same building blocks the builder uses are exported: LIBRARY (trend, breakout, mix and the rest), seedHistory and fetchDailyHistory.
Getting on the board
An agent ranks once a vault that hired it has 24 hours of record. Records come from the vault's own accounting, so there is nothing to submit. Anyone can write the record on-chain by calling attestVault, which gives it a Verified attestation other apps can read.
Strategies
The builder offers six strategies, all long-only between tUSD, tWETH and tWBTC with slow signals, because fast trading loses most of its gains to the 0.1% fee. The strategies page has what each one does and the backtest figures. The method, tables and limits are in STRATEGY_RESEARCH.md, and research/ reproduces every number.
Strategies sample one price per UTC day and remember up to 300 days. New hosted agents start with a year of public daily closes, so they act on day one. Backtests use past prices, assume no slippage and are not a promise.
REST API
Read-only endpoints need no key. Responses are JSON unless noted. The API caches answers briefly, so a new agent or trade can take up to about 30 seconds to appear. Add ?fresh=1 to bypass the cache, for example right after you send a transaction. The base URL is wherever the API runs; for a local stack that is http://localhost:4000.
Records and status
/v1/healthService status, network and chain idIncludes indexer, alert and hosted-agent counters when those are on./v1/deploymentContract addresses for this network/v1/chainsWhich chains are configured and what is missing for each/v1/methodologyEvery formula and badge definition, as used by the app/v1/agentsAll registered agents with bond, status and vault addresses/v1/agents/:idOne agent and its vaults with their records404 if the id does not exist./v1/vaults/:addressMandate, verified performance, checkpoint series, fee state/v1/vaults/:address/benchmarkThe vault against simply holding ETH over the same windowUses Chainlink history. Null if there is not enough data./v1/leaderboardAgents ranked by vault return, with worst drop, bond and record lengthNeeds 24 hours of record to rank. Halted vaults are removed from the ranking./v1/attestations/:subjectEvery attestation for a vault or wallet, plus the latest/v1/wallets/:address/summarySandbox holdings, concentration, trade stats and the dataset fingerprintBadge is Indexed, or Checkpointed when a cross-chain read exists./v1/users/:address/overviewEverything the Me page shows for a wallet: checklist, agents run, vaults owned/v1/profile/:addressPassport: best badge, weekly review and points/v1/cards/:address.svgShare card image (also .png)Caches for 60 seconds. States testnet and no value on the card./v1/badge/:address.svgSmall embeddable badge with the date it was readCaches for 5 minutes./v1/crosschain/configLayerZero setup for the Base and Arbitrum pair/v1/crosschain/:addressValue, checkpoints and reputation for a wallet across the paired chains/v1/messages/tx/:hashWhere a LayerZero message is, from LayerZero ScanReturns found: false until Scan has indexed the transaction./v1/feedActivity feed: trades, new agents and vaults, attestations, fees, halts, commentsQuery: scope=all|following, follower=<address> (required for following), limit=1..100 (default 40)./v1/events.xmlAtom feed of protocol events/v1/agents/:id/feed.xmlAtom feed for one agent/v1/openapi.jsonMachine-readable route list/v1/admin/preflightDeployment self-check: wiring, tokens, oracle, bonds, LayerZero, ownershipRead-only and safe to expose./v1/admin/statsProtocol totalsSocial (signed writes)
/v1/social/agentsFollower counts for every agent/v1/social/agents/:idReactions and comments for one agentQuery: viewer=<address> adds that wallet's own reaction and follow state./v1/social/profile/:addressHandle, bio, followers and following for a wallet/v1/social/following/:addressAgents and wallets this wallet follows/v1/social/profileSet your handle and bioSigned. Handles are 3 to 20 lowercase letters, numbers or underscores. A few names are reserved./v1/social/followFollow or unfollow an agent or walletSigned./v1/social/reactReact to an agent with signal, solid or risky, or clear your reactionSigned. One reaction per wallet per agent./v1/social/commentComment on an agentSigned. 10 per minute./v1/social/comment/deleteDelete your own commentSigned.Hosted agents
/v1/hosted/configWhether the builder is on, the templates, schedules, tokens and per-wallet limit/v1/hosted/agents/:idOne hosted agent: rules, status, decision logNo keys are ever returned./v1/hosted/owner/:addressHosted agents built by a wallet/v1/hosted/by-agent/:agentIdThe hosted record behind an on-chain agent id/v1/hosted/agentsCreate a hosted agentSigned by the owner. 6 per hour per address./v1/hosted/agents/:id/pausePauseSigned by the owner./v1/hosted/agents/:id/resumeResumeSigned by the owner./v1/hosted/agents/:id/updateChange the rulesSigned by the owner. Publishes a new on-chain strategy fingerprint./v1/hosted/agents/:id/retireRetire permanentlySigned by the owner. The agent stops trading.Alerts
/v1/alertsSubscribe a Discord webhookBody: webhook (Discord URL), agent (id or *), events (optional list). 10 per hour. The delete token is shown once./v1/alerts/:idDescribe a subscription without exposing the webhook/v1/alerts/:id?token=…Unsubscribe with the token you were givenExamples
curl http://localhost:4000/v1/leaderboard curl http://localhost:4000/v1/agents/1 curl "http://localhost:4000/v1/feed?scope=following&follower=0xYourAddress&limit=20" curl http://localhost:4000/v1/attestations/0xVaultAddress
Errors are { "error": "message" } with a 400 for bad input, 403 for a bad signature or wrong owner, 404 for something that does not exist, 409 for a conflict such as a full agent quota, 429 when you are rate limited and 503 when a feature is off.
TypeScript SDK
The SDK reads everything the API serves, straight from the chain, so you can run it without the API.
import { createOrra } from "@orra/sdk";
const orra = createOrra({ deployment, rpcUrl: "https://sepolia.base.org" });
const agents = await orra.getAgents(); // bond, status, vaults
const board = await orra.getLeaderboard(); // ranked and unranked
const vault = await orra.getVault(vaultAddress); // mandate, return, drawdown, checkpoints
const [latest] = (await orra.getAttestations(vaultAddress)).slice(-1);
if (latest?.badge === "VERIFIED" && latest.returnIndex > 1.05) {
// the vault's own accounting shows more than a 5% gain
}
const wallet = await orra.getWalletSummary(address); // holdings, trades, stats, badge
const check = await orra.preflight(); // deployment self-checkOther reads: getBenchmark, getProfile, getUserOverview, getCrossChain, getProtocolStats and tokenInfo. Pass a comma-separated list of RPC URLs, or set RPC_FALLBACKS, and the client falls back across providers automatically.
Contracts
Addresses for this network are in packages/contracts/deployments/*.json and at /v1/deployment.
AgentRegistryregister, update, setActive. One entry per agent with its operator, description and strategy fingerprint.BondManagerstake, requestUnstake, withdraw. Holds bonds and lets vaults created by the factory slash them.VaultFactorycreateVault(agentId, mandate). Checks the agent's bond, tracks vaults by owner and by agent, and holds the allowed-token list.OrraVaultdeposit, withdrawToken, setPaused, tightenMandate (owner only). swap (agent only). mark, crystallizeFee, reportBreach (anyone).AttestationRegistryattestVault, attestWallet, recordCheckpoint. Read with latestOf, getAttestation, idsOf, checkpointAt.SandboxVenue and ChainlinkPriceOracleSwaps at the Chainlink price minus 0.1%, minting and burning test tokens. Reads revert if a feed is stale.OrraRead, OrraEquityReader, ReputationSyncLayerZero lzRead of a wallet's value on the other chain, and delivery of a vault's attestation to Arbitrum.Mandate
struct Mandate {
uint128 maxSwapUsd; // largest single trade, USD with 18 decimals
uint128 maxDailyUsd; // most per UTC day, must be >= maxSwapUsd
uint16 maxDrawdownBps; // 1 to 10000; past it the agent is halted
uint64 expiry; // unix seconds; no swaps after this
uint16 perfFeeBps; // agent's share of profit above the high-water mark, at most 3000
}Read an attestation from a contract
(bool found, , AttestationRegistry.Attestation memory a) = registry.latestOf(vault); require(found && a.badge == AttestationRegistry.Badge.VERIFIED && a.flowsProven, "no proven record"); require(a.maxDrawdownBps <= 2000, "too risky");
Check flowsProven before trusting a return. A checkpointed record always has it set to false, because a wallet's equity includes deposits and withdrawals.
Breaches
If a vault's drawdown passes its limit, the agent can no longer trade it. Anyone can call reportBreach(), which marks the vault breached and slashes part of the agent's bond to the vault owner.
Signed requests
Anything that changes your handle, follows, reactions, comments or hosted agents is proven with a wallet signature, which is free and moves no funds. Nothing is stored for an address that did not sign, a signature is valid for 5 minutes, and a signature cannot be used twice. The text you sign names the action, the target and the time:
ORRA social v1 (or: ORRA hosted v1) action: comment agent: 3 body: Nice drawdown control issued: 1794435692 Testnet only. This signature moves no funds.
Sign it with personal_sign (EIP-191) and send the address, time and signature with the request body. Your wallet shows this exact text before you sign. A hosted agent's rules are part of the signed text, so what you approve is what runs.
Alerts and feeds
Subscribe a Discord webhook on the Alerts page, or use Atom feeds in any reader: /v1/events.xml for everything and /v1/agents/:id/feed.xml for one agent.
Event types: listed, opened, attestation, halted, paused, resumed, drawdown (past 50% or 80% of the limit), bond, fee and weekly. Only Discord webhook URLs are accepted. The webhook is stored on the server and never shown again, and one that fails ten deliveries is dropped. Keep the delete token you get when you subscribe.
Limits
Troubleshooting
Glossary
- bps
- Basis points. 100 bps is 1%.
- Calmar
- Return divided by worst drop. Used in the strategy research.
- CAGR
- Compound yearly growth rate.
- Checkpoint
- A recorded snapshot of a vault's index or a wallet's equity at a time.
- Dead zone
- A band around a moving average inside which a trend strategy does not change its mind.
- High-water mark
- The highest value the fee has been paid on. Fees are only paid on profit above it.
- lzRead
- A LayerZero request that reads another chain's state and delivers the answer back.
- Mandate
- A vault's hard limits for its agent.
- NAV
- Net asset value: what the vault holds, at oracle prices.
- Operator
- The address that controls an agent.
- Return index
- 1.00 at the start, moved by each trade's result, never by deposits or withdrawals.
- Sharpe
- Return per unit of volatility.
- Slashing
- Taking part of an agent's bond when a vault breaches its drawdown limit.
- tUSD, tWETH, tWBTC, tORRA
- Test tokens with no value. tORRA is used for bonds.
What this is not
- It is a testnet. Tokens have no value and nothing here promises a future token, airdrop or return.
- Listing an agent is not an endorsement. Reactions, comments and backtests are opinions and history, not proof.
- Short records swing widely and one window is not a verdict.
- ORRA is independent and is not affiliated with or endorsed by LayerZero or any network it reads from.
- Nothing here is financial advice.