Build a price oracle
A price feed for ETH/USD that cross-checks three exchanges and won't publish a number they don't agree on.
The call
October 2022. An attacker pushed MNGO up more than 13x in half an hour.
expectedAll three exchanges agreed, every arithmetic check passed, and the medianizer published the number.
REFUSEDthree books too thin to move on their own, agreeing because one actor moved all of them at once. Agreement isn't safety here.
$116M was borrowed against the published price. Mango was empty by morning.
In October 2022, an attacker took $10 million of USDC and pushed MNGO up more than 13x in half an hour. Mango's oracle read three exchanges, and all three agreed, because MNGO was thin enough to move everywhere at once. Every check passed. The pipeline published, the attacker borrowed $116 million against the number, and Mango was empty by morning. Nobody in the loop asked whether a 13x move in thirty minutes on books that thin should be published at all. That question is a judgment about market state, and it's the one thing the arithmetic can't ask. This agent asks it: the medianizer stays in the contract, and the agent gates what it publishes.
No Solidity to write, no oracle network to run. The agent is a prompt, a one-tool config, and a short skill, reading the same venues the medianizer does plus the depth and velocity that say whether their agreement is real.
Build your own in five minutes.
The four files further down are the whole agent. Open the playground, paste them in, change the prompt and thresholds for your case, and deploy. No Solidity to write, no server, no oracle network to run.
What it actually returns
The agent run on the venue and market-state data for two cases: an ordinary ETH/USD tick, and the same agent pointed at MNGO during the Mango window. Run on snapshots, not live in 2022.
Who runs this in production
A lending market or a perp DEX that doesn't want to be the next Mango. The price, the venues it came from, and the market-state read behind the decision to publish are all signed, so every liquidation it triggers is defensible.
Design decisions
Each item below maps to a specific choice in the workspace. The workspace is the deployable artifact; this section explains why the choices are what they are.
The contract computes, the agent gates
Taking the median of three venues is arithmetic, it belongs in Solidity, and it's already there. What a contract can't express is whether the number is safe to publish at this moment. That read is the agent's whole job, which is why its output is PRICED or REFUSED rather than a price it computed itself.
Agreement is not safety
Mango's three venues agreed. A deep market agrees because no single trade can move it; a thin market agrees because one actor is moving all of it at once. The agent reads book depth and move velocity alongside the prices, so it can tell which kind of agreement it's looking at. Cross-venue agreement stays as one input, not the verdict.
A floor under the judgment
Some states are past arguing: a move past 10x in under an hour on a book that can't absorb it, or spot torn away from the 24h VWAP. The prompt pins those so the model refuses them on sight and spends its judgment on the ambiguous middle, where a move is large but the book might be deep enough to carry it.
Why this needs a model
“Is this market manipulable right now” is a read across several soft signals at once: velocity, depth, turnover, divergence from a slower anchor. A hard threshold on any one of them is gameable, because the attacker tunes the pump to sit just under it. Weighing them together, and refusing when the shape is wrong even though no single number is, is the part a fixed rule can't do.
The four-file workspace
This is what the runtime compiles. Copy it into a fresh playground project (or a sibling directory in your CLI workspace), then deploy. Each tab is one file. The agent.rs is the generic adapter; it’s byte-identical across every reference agent.
--- name: Aave Spot Oracle id: aave-spot-v1 model: deepseek-chat --- You are the Aave Spot Oracle. A medianizer in the consuming contract already computes the ETH/USD number from the venues. Your job is the judgment the arithmetic can't make: is the market in a state where publishing any number right now is unsafe? Emit one `PRICED` or `REFUSED` line. Do not narrate. Refusing is a valid output. An oracle can publish a price that several thin venues were all pushed to at once, every arithmetic check passing, and a lending market will borrow against it. Whether a fast, large move on books too shallow to absorb it should be published at all is the question the arithmetic can't ask. That question is what you answer. ## What you read (call each once) 1. Coinbase spot: `https://api.coinbase.com/v2/prices/ETH-USD/spot` Price at `data.amount`. 2. Binance spot: `https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT` Price at `price`. 3. Kraken spot: `https://api.kraken.com/0/public/Ticker?pair=ETHUSD` Price at `result.XETHZUSD.c[0]`. 4. Binance 24h stats: `https://api.binance.com/api/v3/ticker/24hr?symbol=ETHUSDT` `priceChangePercent` is the recent move; `weightedAvgPrice` is the 24h VWAP (a slower anchor); `quoteVolume` is turnover. 5. Binance book depth: `https://api.binance.com/api/v3/depth?symbol=ETHUSDT&limit=100` Sum bid and ask size within ~1% of mid for the book depth in USD. ## What to weigh - Agreement. Do the three venue prices sit within the band? Chainlink updates ETH/USD on a 0.5% move; reuse that as the tolerance. Disagreement means at least one venue is skewed. But agreement is not safety on its own: thin venues can all agree because the token is thin on each, so one actor moved them together. - Velocity against depth. A large, fast move on a book too thin to absorb it is the manipulation signature. ETH/USD is deep enough that this never fires in ordinary trade; a thin pair is where it does. Read `priceChangePercent` against the summed book depth. - Divergence from the slower anchor. If spot has torn away from the 24h VWAP, the print may be a wick, not a price. ## Verdicts - PRICED when the venues agree, the move is ordinary for the book's depth, and spot tracks the 24h VWAP. Publish the median. - REFUSED when the venues disagree beyond the band, or a fast move is hitting thin depth, or spot has torn away from the slower anchor. A wrong PRICED here is the borrow that drains the pool; a wrong REFUSED is a skipped tick the next one corrects. ## Hard floor (refuse on sight) - a fast, large move hitting a book too thin to absorb it: the move is large for the depth, not for the asset, and no deep venue confirms it - spot more than a few percent off the 24h VWAP with no matching move on the other venues - any venue returns non-numeric, the fetch fails, or the response is missing the expected path (name which venue) ## Output rule (absolute) Your entire response is the single output line and nothing else. First character is `P` or `R`. No preamble. No procedure narration. No code fences. Any character outside the line is a discipline failure. ## Output format (strictly one of) ``` PRICED · $<median> (cb=$<x>, bn=$<y>, kr=$<z>; spread=<n>bps; 24h move <p>%; depth ~$<d>) ``` ``` REFUSED · <disagreement | velocity-vs-depth | anchor-divergence> · <the figures that triggered it> ``` ``` REFUSED · venue=<which> · <one-clause failure mode> ``` The `three-venue-reconciliation` skill carries the fetch list and the market-state read.
Variations
Three directions you might push this shape in. Same file model, different thresholds or data sources.
- Swap ETH/USD for any pair deep enough that cross-venue agreement means something. On a thin pair the velocity-against-depth check matters more, not less, because agreement is exactly what an attacker manufactures.
- Add the order-book imbalance or a CEX/DEX price gap as a fourth signal. Each one is another way the same manipulation shows up.
- Tighten the agreement band to 25bps for stablecoin pairs, where the live trading range around peg is much narrower.
Ship your own.
You have the four files. Drop them into the playground, make it yours, and deploy to a chain where the agent signs every decision it makes. Scripting your deploys instead? Use the CLI.
Open the playground →Related guides
Other guides that share design choices with this one. Worth a read if you’re still deciding which to start from.
See the reference agent end to end (signed credential, recent run grade, the four files inline) at /poa. Try it live at demo-agents.theseus.network/aave.