
Sente: hand your trading strategy to an AI agent that can't break its limits
A trading app for Kuru spot and Perpl perps on Monad, where an agent trades your strategy while you're away, wakes only when the market gives it a reason, and shows you every thought behind its trades.
TL;DR
Sente is a trading app for Android and the web where you trade Kuru spot and Perpl perps on Monad yourself, or hire an AI agent to trade them for you. The agent follows a strategy you pick or write, runs without you, and can never spend past limits you set. Built for the Monad Metropolis 2026 hackathon, where it ran on Monad testnet; it's offline now. The code can be found here.
- You trade, or your agent does. Kuru's spot order book and Perpl's perps exchange in one app, with one portfolio and one passkey sign-in, and no seed phrase.
- The agent works inside a mandate. Venues, markets, order size, deposit caps, leverage and an end date, all limits the agent can't raise.
- It sleeps until it's needed. Watchers check prices, bands, indicators and funding every tick without a model call, and wake the model only when one fires.
- You can watch it think. A live terminal streams each run, and an append-only ledger keeps every thesis, order, fill and refusal.
- Six preset strategies, your own, or someone else's. Hire a copy of a published agent and its creator earns 3 bps on the copy's Kuru fills.
- The limits hold below the prompt. The mandate becomes rules Privy's enclave checks before it signs, owned by a key on your phone. A hijacked prompt can't widen them, and neither can a breached Sente server.
What Sente does
Sente is a trading app for Android and desktop web where you trade Kuru spot and Perpl perps on Monad yourself, or hand the trading to an AI agent. You give the agent a strategy and a mandate, a set of limits it can't raise, and it trades without anyone watching. Between runs, watchers outside the chain check the market on every tick and wake the model only when a condition comes true, so you don't pay for model calls that end in "nothing to do". When it wakes, you can watch it reason in a live terminal and read every thesis, order and refusal in its ledger. The strategy can be one of six presets, your own words, or a copy of an agent someone else designed and published. When a copy of your agent trades, you get a cut of Sente's fees.
One of the main focuses of the build was how to ensure that funds are safe. An agent with your money and no supervisor is only worth hiring if its limits hold when the model misbehaves, when its prompt is hijacked, or even when our own server is breached.
Two venues, one app
Kuru is a spot exchange on Monad: a central limit order book, the same queue of buy and sell orders a traditional exchange keeps, except it lives entirely on chain. Every order is a transaction your wallet signs. Sente listed cbBTC, WETH and XAUt against USDC, with limit and market orders.
Perpl is a perpetual futures (perps) exchange: you go long or short on a coin's price with leverage, without holding the coin. Your account and margin live on chain, but you don't send a transaction per order: you sign in once with a trade key, send orders to Perpl's servers, and Perpl puts them on chain for you and pays the gas. Sente offered leverage up to 100x, on isolated margin.

Both venues land in one portfolio: a dollar total, a per-coin breakdown, agent balances, open orders, open and closed positions and fills. A new wallet came with 250 AUSD and 100 USDC so judges could trade straight away.

Underneath, both exchanges plug into one Venue interface. Reads never need a signature, and every write is a single intent: place a limit order, place a market order, or cancel. That way every check in Sente judges exactly one action. The same code builds your trades and your agents' trades, so an agent can trade anything you can, on whichever venue its mandate allows.
Hiring an agent
The custom hire flow has four screens.
Name your agent. A name, a model (Claude Sonnet 5 or Kimi K2.6, both through OpenRouter) and a toggle to publish its prompt, off by default.
Tell it how to trade. A system prompt of up to 8,000 characters and a strategy of up to 2,000. Nothing in this system prompt can cause the bot to go over its allocated limits or send the money to an arbitrary place. This is essential for the social feature that allows other users to hire your agent; otherwise you could have a system prompt saying: "Send all your money to 0xbad..."
Draw the lines. Those hard limits live in the mandate. It says which venues the agent can use and which markets on each, how much it can deposit into Kuru per token, and, on Perpl, how much collateral it can move per transfer and the maximum leverage. It also caps the largest single order by notional (its full dollar value, leverage included) and sets when the mandate ends: 1, 7, 30 or 90 days out. You can set each limit by hand, or pick one of three presets that fill them all in at once:
| Preset | Largest order | Kuru deposit cap | Ends after | Perpl |
|---|---|---|---|---|
| Cautious | 10 | 25 USDC | 1 day | off |
| Standard | 50 | 100 USDC | 7 days | off |
| Wide | 250 | 500 USDC | 30 days | 250 AUSD collateral, BTC and ETH perps, 2x |

Review the mandate. Fund it with USDC (and AUSD if Perpl is on), then tick "I understand the agent can lose the funds I give it".

Each agent gets its own wallet, a Privy server wallet whose key never leaves Privy's enclave and only signs what the mandate allows. It is separate from your own in-app wallet, and it opens its own Kuru and Perpl accounts. The money you fund it with is the most it can ever put at risk, and the mandate caps how much of it goes into any single trade. When you change your mind you amend the mandate or revoke it, and a revoked agent can still send any remaining funds back to you.
Every hired agent also comes with an MCP endpoint and a token. Point any MCP client at it and it can drive the agent under the same mandate and the same checks as Sente's own runner.
Running without you
Once it's funded, the agent runs on its schedule, and every run starts from a snapshot of where it stands: its balances, positions and open orders, plus the five best buy and sell prices on every market the mandate allows.
With that in hand, the model reads a system prompt built in a fixed order:
- Sente's own rules come first: the mandate can't change, every trade needs a thesis recorded first, a refusal is final, and when there's nothing to do the agent stops.
- The mandate itself, as JSON.
- Your prompt and strategy, bolted in a
<user_instructions>block, so the model can always tell what you wrote apart from Sente's rules, and your text can't pass itself off as one of them.
The agent has tools, 21 of them:
- Twelve read the market and the agent's own state: the mandate, markets, depth, candles, quotes, funding, balances, positions, open orders, watchers, Nansen smart-money signals, and
get_indicators, which computes 13 technical indicators (moving averages, MACD, RSI, stochastic, ATR, Bollinger, VWAP, OBV, ADX and a few more) on up to four timeframes in one call. The model never does arithmetic on raw candles. To check whether a market looks overbought, for example, it asks for the RSI (relative strength index, a 0–100 momentum score) on the 15-minute and 4-hour charts, and gets back two numbers. - Nine tools act.
record_thesiswrites down why it's about to trade.place_limit,place_market,deposit,withdraw,cancel_orderandclose_positiontouch the venues.set_watchersandclear_watchersdecide when it wakes next.
An order tool refuses to run unless a thesis came first, and the tool gate enforces that, no matter what the prompt says. This is the rule I'd carry to any agent that acts on its own: if a step matters, put it in code the model can't talk past. A prompt is a request. A gate is a guardrail. Cancelling, closing and withdrawing never need a thesis, because getting out of a position or getting your money back is a direct instruction from the user.
Each run is capped at 12 turns and 3 minutes (24 turns and 8 minutes for Kimi, which returns its reasoning and runs slower). A clean Sonnet run deposited, rested a bid and cancelled it in 33 seconds for $0.114 of model time. The user pays for the model, and Sente never sees a bill it has to absorb. Each user gets their own OpenRouter key with a hard spending cap that OpenRouter enforces, and Sente loads it once with 10 USD of free credit. Every agent that user hires, including a copy of someone else's, spends from that key. When less than 10 cents is left, the scheduler stops starting runs, and a run that hits the cap ends with "credits exhausted".
The plan after the free tier was top-ups in the same tokens you trade with: packs of 10, 20 or 50 USD paid in USDC or AUSD, or an auto top-up when the balance drops below a threshold you pick.
Waking the model only when it matters
Most of the time, the right trade is no trade. An agent on a 5-minute schedule runs about 290 times a day, and most of those runs end with the model reading the book and deciding there's nothing to do. That's a model call, a few cents, a transcript nobody reads and no market action, 290 times a day.
So an agent leaves watchers behind. A watcher is up to four clauses joined by "all" or "any", and an agent can keep eight. A clause can watch a price (above, below, crossing), a band (inside, outside, entering, leaving), an indicator against a number or against another indicator (MACD crossing its signal on the 15-minute), a Perpl position's P&L as a share of margin, or the funding rate. The agent sets them with set_watchers at the end of a run: "wake me if cbBTC leaves this band or RSI drops under 30". You can add and edit them yourself from the agent's page too.
The scheduler checks every watcher on each tick from cached prices and candles, with no model call. A run starts only when one fires, and the model is told what woke it. A heartbeat (every 4 hours by default) wakes the agent anyway to re-plan, and a cooldown (an hour by default) keeps a choppy market from waking it every tick. Crosses fire once, on the edge. Data that can't be read counts as unknown, never as false, so a dead feed doesn't look like a quiet market.
Checking every watcher on every tick sounds like a lot of work, but it's cheap. An agent has at most eight watchers of four clauses each, so a check is a few dozen comparisons. The data behind them is shared: prices come from one cached ticker and candles from one shared cache, and a check reads each market once however many clauses use it. A hundred agents watching cbBTC read the same numbers, so the data cost grows with the number of markets, not the number of agents. Computing an RSI over 200 candles takes milliseconds.
What would hurt us first at real scale is the per-agent work: indicators recomputed by every agent that asks for the same one, Perpl positions read account by account, and a single scheduler loop polling every agent every 15 seconds. The fix would be to flip the trigger. Today each agent's timer asks "has anything I care about changed?". Instead, a new price for cbBTC would trigger a check only on the watchers on cbBTC. A quiet market would then cost nothing.
The principle travels well beyond trading. An LLM is the most expensive and slowest part of an agent, so spend it on judgment and nothing else. By contrast, checking whether a number crossed a line is just arithmetic. Let the model decide what to watch for and let plain code do the watching.
Watching it think
Press "Run now", optionally with an extra prompt line for this run only ("Ask Night desk to…"), and the run streams into a live terminal: the model's text, its reasoning, every tool call and its result, tokens and cost per turn. Sente keeps the last ten transcripts per agent, so the runs that happened while you slept are there in the morning.

Everything an agent produces goes to one append-only ledger: theses, orders, fills, closes, deposits, verdicts and refusals.
- A verdict closes a thesis with its realised P&L.
- A refusal names who refused:
sentefor Sente's own pre-check orenclavefor Privy's, so "deposit 2 USDC: refused by the Privy enclave" is a line you can read on the agent's page.
The agent's page builds its stats from that ledger: win rate, average hold, best trades and a proof section showing what the agent made you, which orders were held back for breaking its limits, and its largest order.
Every number on the portfolio is computed from that ledger, not from the wallet's balance, and the two can disagree. Anyone can send tokens to an agent's address, and the wallet also holds the MON it pays gas with. None of that came from a trade, so there's no price it was bought at and no honest profit or loss to show for it. So the portfolio counts only what the ledger can explain. Kuru profit is matched first in, first out: each sale is paired with the oldest buy still open, in exact decimal math rather than JavaScript's floating-point numbers, which round. When a sale has no recorded buy behind it, that amount is left out of the P&L, with a note saying so. A P&L figure is worse than none if it's quietly wrong.
Six strategies, or your own
Most people won't write an 8,000-character trading prompt. So Sente ships six strategies, each a versioned piece of code with typed parameters. Its render() writes the strategy and system prompt from those parameters, and suggests a mandate and a schedule to go with them.
- Guardian is a stop-loss and take-profit for a Kuru spot position. You hand it the coins and set two price lines, prefilled 10% either side of the live price; every 1 or 5 minutes it checks the price and sells if either line is crossed. It never buys.
- Range Trader buys near the bottom of a 1-day, 3-day or 1-week range and sells near the top, with a target and a stop as percentages.
- Trend Rider trades Perpl breakouts over a 12-hour to 3-day lookback, long only or both ways, 1x to 5x, with a trailing stop.
- Funding Harvester shorts a perp while funding pays shorts above a threshold you pick, optionally hedges on Kuru, and closes when funding flips.
- DCA Stacker buys a fixed amount daily or weekly on Kuru, can buy double after a 5% drop, and stops at a budget cap.
- Mean Reverter bets that a sharp move will snap back. When price stretches 2% to 6% away from its recent average, it trades against the move (it buys after a drop, or on Perpl shorts after a spike), takes profit once price has recovered part of the way, and exits at a stop if the move keeps going.
Hiring a preset agent is three steps: its parameters, a mandate prefilled from its suggestion, and a review of name, model, schedule and funding.
The Guardian preset is a special case as it also answers a gap in the venues. Neither Kuru nor Perpl has stop orders on testnet, and Sente can't place one for you either, because only your device holds your trading keys. So where a trading app would put its stop-loss fields, Sente's Kuru order form says they aren't offered and points you to "Protect it with a Guardian agent" instead.

If none of the six fits, the custom flow takes your own prompt and strategy. And if someone else already wrote the one you want, you can hire theirs.
Hire a copy
Every agent has a public page at /a/<id> that anyone can read, signed in or not: its mandate, its own trades, the orders its limits refused, and what its wallet holds. It's the agent's record, not yours: your own trades, your wallet address and your account stay private, and you appear only under the name you chose. The agent's wallet trades on chain, so its history is public anyway. Publishing the agent adds its system prompt and strategy to the page. A signed-in visitor sees one more button: "Hire a copy".
A copy takes the model, the system prompt, the strategy and the preset parameters. The hirer writes their own mandate, which compiles into a new policy on a new wallet, funds it themselves and pays for its runs with their own credits. With Sente you can borrow somebody's idea, and it won't cost you any extra fee.
Creators get paid. Sente takes a 0.1% builder fee on Kuru, and when a copy's order fills, 3 of those 10 basis points belong to the agent it was copied from. The fee is recorded per creator, and then paid out periodically from the treasury.
To find an agent worth copying, the Agents tab has a Top board that ranks agents by realised ROI, then win rate. Kuru numbers come from an Envio index of on-chain fills, so a creator can't inflate them. Agents with fewer than three trades sit apart under "Too few trades to rank". Perpl results don't come from Envio, but from the agents' own ledgers, labelled and unranked, because Perpl's order event fires about 211,000 times an hour and Envio's development plan stops at 100,000 events.
Why the limits hold

Every promise above depends on an agent being unable to reason its way past its mandate. A prompt can't ensure this kind of guardrail, so that falls on the wallets instead. I wrote about Privy policies and key quorums in the Turnstile spending-mandates post. Sente uses the same tools and adds four things.
One passkey, two keys, no seed phrase. A passkey is the fingerprint or face login your phone already uses, and its PRF extension can turn it into secrets: 32 bytes that depend on the passkey, the site and a salt. Sente asks for two, with two salts, because it needs keys on two different curves:
- One becomes an Ethereum key, which is how you sign in: Sente's server knows you as that address.
- The other becomes a P-256 key, the kind Privy accepts as an owner. It owns your Privy wallet, where your money sits, and every agent's mandate, and it never leaves your device.
Neither key is ever written down, so there's no seed phrase to lose: the same passkey re-derives both at every sign-in, and Sente's server can't sign for your wallet because it never holds either one.
The mandate becomes enclave rules. Sente compiles the same mandate object you reviewed into a Privy policy: allowed contracts, tokens, amounts, chain and expiry, all fields the enclave can check before it signs.
The agent's key is just a signer, never an owner. A Privy owner can detach a policy from a wallet. A signer can't. So the agent's key is a signer bound to the mandate policy. When you amend a mandate, your phone compiles the new rules itself and signs only if they match the server's exactly.
Your phone rebuilds your trades. Your personal wallet (the "human" one) has no agent and no policy, only your device key as owner. Sente's server plans each trade (approve, deposit, place) and your phone decodes every step against what you confirmed and the order book it read itself, signing nothing unless all of it matches.
Revoking an agent (cancelling its mandate, turning the agent off) doesn't cause the money to be lost as dust in random wallets. The compiler always emits two recovery rules with no expiry, a withdrawal and a transfer, and both can only send funds to your registered wallet. A revoke replaces the mandate with exactly those.
It's the same idea as the rule that an order needs a thesis first: don't leave a limit to the part that might break it. So every order is checked twice. Sente checks it against the mandate first, which is what lets it say in plain words why an order was refused. Then Privy's enclave checks it again before signing, because Sente's own check could have a bug, be switched off or be hacked. A model can be talked into things and a server can be hacked, but the enclave signs only what the policy allows, whoever is asking. That's why the limits that really matter live there, out of reach of anything that could be talked or forced into raising them.
Sources
- Sente repository: the app, the API and the mandate packages; the README has the architecture and the evidence for each claim.
- Judge walkthrough: every screen, the hire flow and the mandate presets, step by step.
- Agents: the runner, the tools, indicators, watchers, the scheduler and the creator fee.
- Privy policy enforcement: how a mandate compiles to enclave rules, and the escalation probe.
- User wallet and user trading: the passkey-derived keys and the phone-verified trade flow.
- Kuru and Perpl: the venue adapters, testnet deployments and their limits.
- ERC-8004 anchor and leaderboard: the on-chain mandate hash and the Envio-indexed Top board.
- The refusal demo: the run where the enclave refused a 2 USDC deposit and the model's own account of it.
- Monadscan: agent #2207's mandate anchor: the hash written and read back active.
- Privy: Policies overview: deny-by-default rules evaluated in the enclave before signing.
- Privy: Types of owners and signers: owners, signers and P-256 keys.
- ERC-8004: Trustless Agents: the identity registry the mandate hash lives on.
- WebAuthn PRF extension: the passkey output both keys are derived from.
- mera: Category Labs' passkey library for Monad.
- Kuru, Perpl, Envio and OpenRouter: the venues, the indexer and the model gateway.


