Robinwood StocksDocs
Feeds

Manipulation cost

What moving each Robinwood Stocks feed actually costs, computed from live pool state and published openly — model, formulas, assumptions, and the honest verdict that today every native feed is thin.

An integrator cannot size the risk of an oracle whose attack cost is a secret. This page publishes the cost model behind the "move 5% costs" column on app.rwoodstocks.xyz/feeds: for every native feed, a lower bound on the capital required to move its TWAP by 1, 5 and 10 percent, recomputed from live pool state every 15 minutes.

The current verdict, stated plainly: today every one of the fifteen native feeds is thin. At the time of writing, moving VIRTUAL's one-hour TWAP by 5% takes on the order of $30k of committed capital; every other native feed sits under $2k, and the thinnest pools can be displaced with pocket change. These feeds are safe for Robinwood Stocks's own use — the consuming contracts cap what any manipulated read could extract through immutable turnover and slippage budgets — and they are usable as display or bounded-risk inputs. They are not liquidation or solvency oracles, and no disclaimer buried in a footer changes that. Hence this page.

The live numbers

  • Human-readable: the column on the feeds page.
  • Machine-readable: GET app.rwoodstocks.xyz/api/feeds/cost — per feed: capital in USD for each target, the required hold time, the method, and the thin flag; plus the model version, the block it was computed at, and the assumptions, so the response is self-describing.

A feed is flagged thin when its 5% figure sits under $100,000. The threshold is deliberately strict: it asks what a serious lending market should demand, not what makes our numbers look good.

The model

Both adapters aggregate over time, so an attacker pays twice: once to displace the spot price, and once to hold the displacement against arbitrage long enough to move the average. We publish the displacement capital exactly (it is computable from live state) and state the hold requirement; we deliberately do NOT model arbitrage losses during the hold, which makes every figure a lower bound.

v4 feeds (1-hour median)

The adapter answers the median sqrtPrice of the observations in a 3,600-second window. A median only moves once more than half the observations read the displaced price — with the keeper recording roughly every five minutes, that means holding the displacement for at least 1,800 seconds of open arbitrage exposure.

Displacement capital, from the pool's live sqrtPriceX96 and in-range liquidity L (target factor m, e.g. 1.05):

push price up   (currency1 in):  Δy = L · sqrtP · (√m − 1) / 2⁹⁶
push price down (currency0 in):  Δx = L · 2⁹⁶ · (√m − 1) / (sqrtP · √m)

The attacker takes the cheaper direction; we publish that minimum, dollarized through the same quote feeds the adapters use. Single-range lower bound: any initialized ticks crossed on the way add liquidity and can only raise the real cost.

v2 feeds (cumulative TWAP)

The adapter averages the pair's cumulative price against a checkpoint at least 1,800 seconds old. Cumulative averages fall to a different attack: a displacement of Y% held for t seconds moves an 1,800-second average by roughly Y · t / 1800. The cheap attack is therefore a trade-off between violence and patience, and we publish the cheapest point on a grid (t ∈ {60, 300, 900, 1800} seconds, displacement capped at 10x price):

input to multiply price by f:  reserve_in · (√f − 1) · 1.003   (0.3% pair fee)

again taking the cheaper of the two directions from live reserves.

What the model deliberately leaves out

Stated so nobody mistakes a lower bound for a promise:

  • Arbitrage losses during the hold. Every second the price is displaced, arbitrageurs can trade against the attacker at a profit. On an active pool this dominates the real cost; on a dead pool it can be near zero. We publish the floor, not a guess about the arbitrage.
  • Tick crossings (v4): liquidity outside the current range raises the displacement cost above our single-range figure.
  • Pool fees on the attacker's own swaps (v4 side; v2 includes the 0.3%).
  • Checkpoint games (v2): checkpointing is permissionless but rate-limited to one per 450 seconds, and the adapter only accepts checkpoints aged between 1,800 and 21,600 seconds.
  • USDG is assumed $1; ETH and VIRTUAL are read from their registered feeds at computation time.

If you find an attack cheaper than our floor, that is a bug in the model: report it and we will correct the model and version it, exactly like any other methodology change.

Why publish an attack roadmap

Everything in this model is derivable from public pool state by anyone competent. Publishing it does not arm an attacker; it removes the information asymmetry between the attacker (who would compute this anyway) and the integrator (who usually cannot). The alternative posture, a safety claim with no number attached, is the one that ends up in someone's post-mortem.

On this page