Skip to content
Bias
DocsPARAMS.md16 min read
On this page · 7

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#

ParameterValueWhy
Reserve per positionmin(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 capmin(2 x treasury budget, MAX_TOTAL_OI_USD), default $20kSmall-size launch. Borrow utilization is measured against this cap.
Open / close fee8 bps / 8 bpsIn line with GMX v1 (10 bps) and Gains (~8 bps); all fees go to the treasury.
Liquidation bounty5 USDG + 10 bps of sizePaid from remaining equity; a fully underwater position still pays the flat 5 USDG out of escrow so third parties always liquidate.
Router delay1 L2 block min, 180s cancel / self-executeKills same-block front-running. Bounded in code: 1–10 blocks, 30s–1h. Self-execute window 1h, acceptable price enforced.
Execution fee0 (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 collateral10 USDGRequest spam costs more than keeper gas.
Price signers3 keys, minSigners = 2One leaked signer cannot move a TWAP-class price alone.
Timelock48h, Safe proposes / executes / cancelsUsers 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.

MarketClassFeedMax levMaint.ReserveOI cap / sideMax net skewSpreadStale spreadStalenessHours
ETHChainlink0x78F3…d3A920x1.0%50%$10k$7.5k30 bps150 bps25hno
BTCChainlink0xa2c5…025120x1.0%50%$10k$7.5k30 bps150 bps25hno
SPYChainlink0x3197…9f6A10x1.0%30%$7.5k$5k30 bps200 bps25hyes
QQQChainlink0x8090…a2ae10x1.0%30%$7.5k$5k30 bps200 bps25hyes
NVDAChainlink0x379E…9F155x2.0%50%$5k$3k30 bps250 bps25hyes
AAPLChainlink0x6B22…2cD05x2.0%50%$5k$3k30 bps250 bps25hyes
GLD, AMC-no feed on this chainskipped
Coins: NOVAAI, BUN (Pons MemeHook), FRONG, PROLOGUE, HOOKR, POOLS (hookless ETH), PONS, AI, BONER (USDG)TWAP (180s)UniV4TwapSourcetier (3x / 5x Large)5.0%100%min($5k, tier cap)$5k50 bps200 bps90sno
Auto-listed coins (MarketFactory)TWAP (180s), keeper-optionalCREATE2 UniV4TwapSourcetier5.0%100%min($5k, tier cap)$5k50 bps200 bps90sno

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:

TierImpact feeImpact depthMax impactPayout breaker (net trader PnL / hour)
Crypto1x$50M virtual1%10% of budget
Index1x$20M virtual1%6% of budget
Stock1x$5M virtual1%3% of budget
Coin2xlive pool depth3%$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):

WhereReverts with
RiskManager.warmupone global valueWarming
OracleRouter.markets(id).twapWindowper market; new markets get it from MarketFactory.template()no price (twap() returns 0)
UniV4TwapSource.depthWindowper source, a constructor argument since 2026-09-23, from the templateNoDepth

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 = 300sW = 180s (launch)W = 60s
keeper cadence that keeps dwell/W near 1/3210-270s75-105s60-75s (hotMinSec 60)15-25s
weight of one observation at that cadence30%35%42%~40%
seconds a dislocation must be held for a 1% mark error at S = +20%45s15s9s3s
mark error from a +20% pump held 15s0.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):

WindowDislocation needed, held 15sDislocation 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:

CapValueWhere
profit on one position100% of net collateral = $250 at 3x on $750vault.maxProfitBpsOf = 10_000
payout from one coin market per hour$1,000RiskConfig.payoutCapUsdgPerHour
payout across all coin markets per day$1,000RiskManager.setGroupBudget(2, ...), COIN_DAILY_BUDGET_USD
escrow all coin positions can win at once$3,000COIN_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.sol derives it as window / 2; test/Factory.t.sol:test_maxObservationAge_staysUnderTheWindow asserts it.
  • The recorder's cadence must stay well under the window. recorder.hotMinSec is 60 (60-75s with jitter), and a market younger than youngForSec (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 and record() 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):

TierConservative depthPer-wallet max position (long + short)Per-coin OI cap per sideMax leverage
below floor< $14,000close-onlyclose-only-
Lite$14,000 - $35,000$300$7503x (LITE_MAX_LEVERAGE=2 for 2x)
Small$35,000 - $175,000$500$1,5003x
Mid$175,000 - $700,000$1,000$3,0003x
Large$700,000+$2,000$5,0005x

Market cap is roughly liquidity / 0.35 for these coins (Lite starts around a $40k market cap, Large around $2M).

ParameterValueWhy
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 budget10% 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 profit100% of net collateralvault.maxProfitBpsOf = 10_000.
Liquidity pulllive liquidity < 50% of window minimumIncreases refused at once.
Warmup180s 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 ceilingup after 24h of continuous observations (gaps <= 6h), down at onceA pump cannot unlock a bigger tier. Auto-listed coins start at Lite; governance coins at Large (live tier decides).
Keeper-optional markets without signerscapped at LitemaxTierWithoutSigners = 0.
Dead coinbelow floor for 3 days, observations <= 6h apartAnyone settles positions in batches.
Delist grace24hUsers close before settlement.
Settlement bounty1 USDG per positionFrom the position's released escrow.
Listing fee0 (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 markets20 (MAX_AUTO_MARKETS)Keeper cost and aggregate risk stay bounded.
Factory quotesnative ETH and WETH (18 decimals, Chainlink ETH/USD, 26h), USDG (6 decimals, $1)
Factory hooksnone, Pons MemeHook (declared 600 bps per leg: 1% hook fee + 5% creator tax)Unknown hooks refused.
Factory token denylist29 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 template180s 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 2Same 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)#

CallGasNotes
PositionRouter.executeIncreasePosition (coin, tier + budget checks)518k - 650kfork rehearsal before the change: 461k - 627k
PositionRouter.executeDecreasePosition (coin, full close)265k
PositionRouter.createIncreasePosition / createDecreasePosition233k - 273k / 201k
MarketFactory.list2.78M - 2.83M2.88M on the mainnet fork (BONER/USDG)
UniV4TwapSource.record72k (85k first in a slot)
RiskManager.poke169k
PerpVault.settle, batch of 10 positions1.01Mabout 100k per position
OracleRouter.postPrice41k - 84kunchanged

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.sol shows 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 size s pays s²/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.setSequencerFeed is wired for when one does.

Source: hoodperps-contracts/PARAMS.md, synced at build. Title in repo: Launch parameters.