Launch parameters
Applied by script/Configure.s.sol. Every number is owner-settable later (behind the 48h timelock). The manipulation
math behind the memecoin numbers is in RISK.md.
Global#
| Parameter | Value | Why |
|---|---|---|
| Reserve per position | min(market reserve % x notional, max profit % x net collateral); max profit 900% (majors), 100% (coins) | Notional cap bounds treasury capital per market; 900%-of-collateral cap (Gains Network's rule) stops a 20x position tying up a full notional. Coins: a trader never wins more than the collateral posted (vault.maxProfitBpsOf). |
| Aggregate OI cap | min(2 x treasury budget, MAX_TOTAL_OI_USD), default $20k | Small-size launch. Borrow utilization is measured against this cap. |
| Open / close fee | 8 bps / 8 bps | In line with GMX v1 (10 bps) and Gains (~8 bps); all fees go to the treasury. |
| Liquidation bounty | 5 USDG + 10 bps of size | Paid from remaining equity; a fully underwater position still pays the flat 5 USDG out of escrow so third parties always liquidate. |
| Router delay | 1 L2 block min, 180s cancel / self-execute | Kills same-block front-running. Bounded in code: 1–10 blocks, 30s–1h. Self-execute window 1h, acceptable price enforced. |
| Execution fee | 0 (settable up to 0.01 ETH) | Pull-paid to the keeper that executes or clears a request; refunded to users who cancel or self-execute. Turn on if request spam appears. |
| Min collateral | 10 USDG | Request spam costs more than keeper gas. |
| Price signers | 3 keys, minSigners = 2 | One leaked signer cannot move a TWAP-class price alone. |
| Timelock | 48h, Safe proposes / executes / cancels | Users see every owner change two days ahead (keeper watchdog alerts on CallScheduled). |
Per market#
Chainlink feeds verified on-chain 2026-09-14 (keeper/fetch-feeds.js, keeper/feeds.json). All 8 decimals; directory
lists 0.5% deviation / 24h heartbeat. Observed: ETH updates every 30s–71min, BTC 36min–3.8h, NVDA/AAPL minutes–4.5h,
SPY/QQQ 6h–24h with a ~59h weekend gap.
| Market | Class | Feed | Max lev | Maint. | Reserve | OI cap / side | Max net skew | Spread | Stale spread | Staleness | Hours |
|---|---|---|---|---|---|---|---|---|---|---|---|
| ETH | Chainlink | 0x78F3…d3A9 | 20x | 1.0% | 50% | $10k | $7.5k | 30 bps | 150 bps | 25h | no |
| BTC | Chainlink | 0xa2c5…0251 | 20x | 1.0% | 50% | $10k | $7.5k | 30 bps | 150 bps | 25h | no |
| SPY | Chainlink | 0x3197…9f6A | 10x | 1.0% | 30% | $7.5k | $5k | 30 bps | 200 bps | 25h | yes |
| QQQ | Chainlink | 0x8090…a2ae | 10x | 1.0% | 30% | $7.5k | $5k | 30 bps | 200 bps | 25h | yes |
| NVDA | Chainlink | 0x379E…9F15 | 5x | 2.0% | 50% | $5k | $3k | 30 bps | 250 bps | 25h | yes |
| AAPL | Chainlink | 0x6B22…2cD0 | 5x | 2.0% | 50% | $5k | $3k | 30 bps | 250 bps | 25h | yes |
| GLD, AMC | - | no feed on this chain | skipped | ||||||||
| Coins: NOVAAI, BUN (Pons MemeHook), FRONG, PROLOGUE, HOOKR, POOLS (hookless ETH), PONS, AI, BONER (USDG) | TWAP (180s) | UniV4TwapSource | tier (3x / 5x Large) | 5.0% | 100% | min($5k, tier cap) | $5k | 50 bps | 200 bps | 90s | no |
Auto-listed coins (MarketFactory) | TWAP (180s), keeper-optional | CREATE2 UniV4TwapSource | tier | 5.0% | 100% | min($5k, tier cap) | $5k | 50 bps | 200 bps | 90s | no |
Launch coins (2026-09-15) and why the others are not listed: script/Configure.s.sol header. PONS, AI and BONER (deepest
pools USDG-quoted) became listable on 2026-09-16 when the source learned USDG quotes; re-run the listing rule before
setting TWAP_PONS / TWAP_AI / TWAP_BONER.
Small-size launch (2026-09-15). Caps above replace the earlier $25k–$200k per side. Every market has a fixed
per-side OI cap and a fixed net-skew cap; script/Configure.s.sol refuses a preset without both. The vault checks the
fixed cap and the depth-derived cap separately, so the effective cap is min(fixed, depth-derived): a depth reading
that is too high (concentrated liquidity, RISK.md §7) can never raise a cap above the fixed number, and a thin pool
still lowers it (test/LaunchCaps.t.sol). Max leverage is unchanged. Worst case per market is bounded by the fixed
cap times the reserve %: majors $5k per side, index $2.25k, stocks $2.5k. Coins: see "Coin risk layer" below.
TWAP pool rules enforced by the scripts and the factory: quote currency is native ETH, WETH or USDG (either side), the hook is none or the Pons MemeHook (factory: governance allowlist), static fee. The depth-share caps (1.5% of depth on Pons pools, 0.3 x LP fee on hookless pools) were removed on 2026-09-16: on untaxed pools they give caps nobody can trade (RISK.md 8.1), and the tier table replaces them with small hard caps and a daily budget.
Risk layer per tier:
| Tier | Impact fee | Impact depth | Max impact | Payout breaker (net trader PnL / hour) |
|---|---|---|---|---|
| Crypto | 1x | $50M virtual | 1% | 10% of budget |
| Index | 1x | $20M virtual | 1% | 6% of budget |
| Stock | 1x | $5M virtual | 1% | 3% of budget |
| Coin | 2x | live pool depth | 3% | $1,000 per market per hour, plus the coin group's daily budget |
A trip pauses opens on that market for one hour (closes are never affected). Wins realized while the same account holds the opposite side count net of that side's unrealized loss.
TWAP class extras: window 180s, divergence breaker 300 bps, spot guard 500 bps, source maxObservationAge 90s,
depth window 180s, cardinality 1440 (24h at the 60s hot cadence).
Coin warm-up (2026-09-20: 1h -> 15 min. 2026-09-23: 15 min -> 3 min)#
A coin market cannot be opened until its source's history covers all three of these, so they are one number
(COIN_TWAP_WINDOW in script/Configure.s.sol, 180s):
| Where | Reverts with | |
|---|---|---|
RiskManager.warmup | one global value | Warming |
OracleRouter.markets(id).twapWindow | per market; new markets get it from MarketFactory.template() | no price (twap() returns 0) |
UniV4TwapSource.depthWindow | per source, a constructor argument since 2026-09-23, from the template | NoDepth |
Before 2026-09-23 depthWindow was hardcoded to 1800 in the source, was not in the template and was not settable
before the source existed, so every fresh listing needed an owner setDepthWindow transaction. It is now a
constructor argument the factory fills from Template.depthWindow, and list() writes observation #1 itself, so a
listing is tradeable exactly one window later with no follow-up transaction of any kind.
The wait cannot be shortened independently of the window. twap(window) returns nothing until the source's
history reaches back window, so "time from list() to first open" and "length of the TWAP" are the same number.
Shortening the wait shortens the average.
What a shorter window costs
One observation is worth dwell / window of the mark, where dwell is how long it stays the newest observation
(floor MIN_RECORD_SPACING = 15s; worst case, the recorder's cadence). So to move the mark by E an attacker must
hold a spot dislocation S for dwell = E/S x window seconds — the window is a linear price on manipulation:
W = 900s (old) | W = 300s | W = 180s (launch) | W = 60s | |
|---|---|---|---|---|
keeper cadence that keeps dwell/W near 1/3 | 210-270s | 75-105s | 60-75s (hotMinSec 60) | 15-25s |
| weight of one observation at that cadence | 30% | 35% | 42% | ~40% |
| seconds a dislocation must be held for a 1% mark error at S = +20% | 45s | 15s | 9s | 3s |
| mark error from a +20% pump held 15s | 0.33% | 1.0% | 1.7% | 5.0% |
The dwell/window ratio is roughly flat across these because the cadence moves with the window; what changes is the
absolute hold time, and that is what arbitrage prices. Holding a 20% dislocation for 45 seconds is a large,
public, arbitrageable position; holding it for 3 seconds is nearly free.
Is it profitable? (Lite caps, coin group budget)
A round trip at the Lite ceiling is $750 of notional (sideCap; walletCap is $300, so three wallets). Fixed cost
of the round trip, before any pool cost:
spread, 50 bps each way (spreadBps) | $7.50 |
| open + close fee, 8 bps each | $1.20 |
skew impact fee at the Lite depth floor ($14k): 2 x 750^2 / 14,000 = $80, capped at maxImpactBps 300 bps | $22.50 |
| total | $31.20 |
So the attacker needs a mark error E > 4.16% on $750 before the pool round-trip cost. With E = (dwell/W) x S,
and taking the worst case of a coin's own lister moving their own thin pool (so the pool fee is the only cost):
| Window | Dislocation needed, held 15s | Dislocation needed, held 60s |
|---|---|---|
| 900s | +250% | +62% |
| 300s | +83% | +21% |
| 180s | +50% | +12.5% |
| 60s | +16.6% | +4.2% |
At 180s a lister has to hold a 50% dislocation of their own pool through a full record spacing, or a 12.5% one for a solid minute, just to break even; the profit above break-even is a few dollars per round trip. At 60s a 4% dislocation held for a minute pays, which is inside normal volatility on a new coin and not something we can distinguish from honest price movement. 60s is not a window I would sign off on at these caps. 180s is.
Deep pools are not the concern at any of these windows: on FRONG-sized depth ($846k) a 5% dislocation costs ~$57 in pool fees alone against a maximum $10 of mark error. The exposure is entirely in coins sitting near the $14k Lite floor, which is also where the impact-fee cap (300 bps) stops protecting proportionally.
The hard ceiling, which the window does not change
Whatever the window, extraction is capped by, in order:
| Cap | Value | Where |
|---|---|---|
| profit on one position | 100% of net collateral = $250 at 3x on $750 | vault.maxProfitBpsOf = 10_000 |
| payout from one coin market per hour | $1,000 | RiskConfig.payoutCapUsdgPerHour |
| payout across all coin markets per day | $1,000 | RiskManager.setGroupBudget(2, ...), COIN_DAILY_BUDGET_USD |
| escrow all coin positions can win at once | $3,000 | COIN_RESERVE_CAP_USD |
So the worst case per coin per day is $1,000 for the whole coin group, not per market — and at 180s an attacker needs ~40 break-even-plus round trips, each requiring a held 50% pool dislocation, to get there. The window decides whether the attack is worth attempting; the group budget decides how bad the day can get if it is.
Keeping the window honest
Two things have to hold, or a short window degenerates into "the last recorded tick":
maxObservationAge(90s) must stay below the window. Between observations the newest tick is extended flat, so a freshness bound longer than the window would let one tick be the entire mark.Configure.s.solderives it aswindow / 2;test/Factory.t.sol:test_maxObservationAge_staysUnderTheWindowasserts it.- The recorder's cadence must stay well under the window.
recorder.hotMinSecis 60 (60-75s with jitter), and a market younger thanyoungForSec(900s) records every 30-45s (youngMinSec) so its first window is an average of 4+ observations rather than the listing tick held flat.list()writes observation #1 andrecord()is permissionless, so the site's "Record a price now" button and the lister themselves can fill the window without the keeper (src/components/coins/WarmUp.tsx).
Cost of the faster cadence: a record is 63,737 gas. At 60-75s a hot market is ~1,280 records/day = 82M gas/day = 0.0046 ETH/day per hot market at 0.0565 gwei, against 0.0009 at the old 210-270s. Idle markets (nothing open) are unchanged at 25-30 min, so this is only paid for markets that actually carry positions. The young cadence adds ~20 records (1.3M gas, 0.00007 ETH) per listing, once.
The spot guard (500 bps) does not cover this attack: getPrice reverts on divergence only for increases, and a
close is always priced. What the guard does do is widen the range against the trader (lo = min(twap, spot),
hi = max(twap, spot)), so a pumped spot never improves the price a long closes at.
Coin risk layer (2026-09-16, RISK.md section 8)#
Liquidity tiers (RiskManager.setTiers, evaluated live on every increase from the conservative depth):
| Tier | Conservative depth | Per-wallet max position (long + short) | Per-coin OI cap per side | Max leverage |
|---|---|---|---|---|
| below floor | < $14,000 | close-only | close-only | - |
| Lite | $14,000 - $35,000 | $300 | $750 | 3x (LITE_MAX_LEVERAGE=2 for 2x) |
| Small | $35,000 - $175,000 | $500 | $1,500 | 3x |
| Mid | $175,000 - $700,000 | $1,000 | $3,000 | 3x |
| Large | $700,000+ | $2,000 | $5,000 | 5x |
Market cap is roughly liquidity / 0.35 for these coins (Lite starts around a $40k market cap, Large around $2M).
| Parameter | Value | Why |
|---|---|---|
| Coin group (2) daily budget | $1,000 (COIN_DAILY_BUDGET_USD) | Net treasury payout across all coin markets, current hour + previous 24; at the budget every coin market refuses increases. |
| Coin group reserve cap | $3,000 (COIN_RESERVE_CAP_USD) | Escrow all coin positions can win at once. Any 24h loss < budget + reserve cap = $4,000. |
| Majors group (1) daily budget | 10% of treasury budget (MAJOR_DAILY_BUDGET_USD) | Same meter for ETH, BTC, stocks. |
| Majors per-wallet cap | $1,000 (MAJOR_WALLET_CAP_USD) | One account's long + short per market. |
| Coin max profit | 100% of net collateral | vault.maxProfitBpsOf = 10_000. |
| Liquidity pull | live liquidity < 50% of window minimum | Increases refused at once. |
| Warmup | 180s of source history (RiskManager.warmup) | New markets refuse opens until the 180s TWAP and the 180s depth window exist. Both come from the factory template, so no owner transaction follows a listing. See "Coin warm-up". |
| Tier ceiling | up after 24h of continuous observations (gaps <= 6h), down at once | A pump cannot unlock a bigger tier. Auto-listed coins start at Lite; governance coins at Large (live tier decides). |
| Keeper-optional markets without signers | capped at Lite | maxTierWithoutSigners = 0. |
| Dead coin | below floor for 3 days, observations <= 6h apart | Anyone settles positions in batches. |
| Delist grace | 24h | Users close before settlement. |
| Settlement bounty | 1 USDG per position | From the position's released escrow. |
| Listing fee | 0 (LISTING_FEE_USDG) | Free for the quiet relaunch: nobody should need USDG in hand to list. maxActive 20 is the spam bound. |
| Max active auto markets | 20 (MAX_AUTO_MARKETS) | Keeper cost and aggregate risk stay bounded. |
| Factory quotes | native ETH and WETH (18 decimals, Chainlink ETH/USD, 26h), USDG (6 decimals, $1) | |
| Factory hooks | none, Pons MemeHook (declared 600 bps per leg: 1% hook fee + 5% creator tax) | Unknown hooks refused. |
| Factory token denylist | 29 tokenized stocks, leveraged / pre-IPO wrappers, stablecoins, wrapped BTC (DENY_TOKENS adds) | Not coin markets; guardian can add. Volume, age and honeypot policy is off-chain. |
| Factory template | 180s TWAP and 180s depth window, maxObservationAge 90s, spot guard 500, keeper-optional, 5x market max (tier lowers), $5k fixed side / skew caps, 5% maintenance, impact 2x capped 3%, payout breaker $1k per hour, group 2 | Same as governance coins except keeper-optional. |
Worst cases (RISK.md 8.4): per coin both sides at once Lite $1,500, Small $3,000, Mid $6,000, Large $10,000; all coin markets in any 24h window < $4,000.
Gas (Foundry --isolate, 2026-09-16)#
| Call | Gas | Notes |
|---|---|---|
PositionRouter.executeIncreasePosition (coin, tier + budget checks) | 518k - 650k | fork rehearsal before the change: 461k - 627k |
PositionRouter.executeDecreasePosition (coin, full close) | 265k | |
PositionRouter.createIncreasePosition / createDecreasePosition | 233k - 273k / 201k | |
MarketFactory.list | 2.78M - 2.83M | 2.88M on the mainnet fork (BONER/USDG) |
UniV4TwapSource.record | 72k (85k first in a slot) | |
RiskManager.poke | 169k | |
PerpVault.settle, batch of 10 positions | 1.01M | about 100k per position |
OracleRouter.postPrice | 41k - 84k | unchanged |
Rationale#
- Spreads 30 bps (was 10–20). The feeds only move when the price has drifted 0.5%. Anyone watching the real price knows the feed is up to 50 bps behind; with a 10 bps spread each way that is a free trade against the treasury. 30 bps each way plus 16 bps of fees makes the round trip cost 76 bps, more than the maximum lag.
- Staleness 25h for every Chainlink market (was a 1h placeholder, then 6h). The mainnet-fork rehearsal on 2026-09-15 showed SPY and QQQ marked stale at 6h: the SPY feed had not updated for 23h, across a full trading session, because the price stayed inside the 0.5% deviation band. The deviation trigger is what keeps a healthy feed current; staleness only detects a dead one. Consequences: after a weekend (observed ~59h gap) equity increases wait for the week's first update, and a dead feed goes unnoticed by the contract for up to 25h. The keeper watchdog alerts much earlier.
- Maintenance 1% (not 0.5%). The treasury eats every gap past the maintenance margin. 20x with 1% maintenance means liquidation at a 4% adverse move, leaving 1% of notional for gap and bounty. Single stocks get 2% and 5x because they gap at the open; memecoins get 5% and 3x because they gap any time.
- Reserve 30–50% for majors. A trader's max profit is the reserve. At 10–20x the 900%-of-collateral cap binds first anyway. Memecoins keep 100% because 2x moves are routine; their exposure is bounded by liquidity-scaled caps.
- Coin caps are tiers, not a share of depth.
test/Attack.t.solshows pump attacks lose money up to 2% of depth at a 5% per-leg tax, but the coins people trade here have no tax, and on a 0.3% fee pool the same rule allows 0.12% of depth. So coins get small absolute caps by liquidity tier, 100% max profit and a shared daily budget, which bound what an attack can take instead of making it unprofitable (RISK.md section 8). - Net skew caps. The treasury only loses on the imbalance between longs and shorts. Skew caps bound that directly while letting balanced interest grow; trades that reduce the imbalance are never blocked and pay no impact fee.
- Impact fee. Adding to the imbalance costs
(skew_after² − skew_before²) / depth. From zero, a trade of sizespayss²/depth, half of a constant-product pool's slippage at 1x; memecoins use 2x so a perp entry is never cheaper than moving the pool itself. - Payout breaker. If traders net more than the cap within a (linearly decaying) hour, opens pause for an hour and the watchdog alerts. Exits keep working. It was a persistent close-only until review finding F3 showed a hedged pair could trip it cheaply and turn it into a 48h outage (only the timelock could re-open).
- Funding. Longs pay shorts (or the treasury when there is no other side) proportional to skew: 0.01%/h at full skew ≈ 88%/yr. Memecoins 5x that.
- Borrow. 9.5%/yr at full utilization on majors, 31% on memes; scales with utilization.
Capacity#
Approved USDG budget / average reserve ratio ≈ tradable OI. At launch the $60k aggregate cap binds long before the budget does. The aggregate cap is shared by majors and coins, including auto-listed coins. Raise caps in steps through the timelock as the tax-funded treasury grows, and before raising any depth-scaled market above a few thousand dollars per side, address the concentrated-liquidity and hook items in RISK.md §7.
Still unverified#
- Whether the "RH" stock feeds price Robinhood's tokenized stocks 24/5 or only in NYSE hours (affects after-hours gap risk).
- Gold and AMC feeds: watch the Chainlink directory (
feeds-robinhood-mainnet.json); SLV and GME exist as alternatives. - No L2 sequencer uptime feed exists on this chain;
OracleRouter.setSequencerFeedis wired for when one does.