Launch and money flow
Money flow#
$BIAS on Pons: 5% creator tax on buys and sells (ETH)
│ claim()
▼
CUSTODY WALLET (yours, cold) ── no contract ever holds an allowance on it
│ you convert ETH → USDG and top up on your schedule (weekly)
▼
TREASURY WALLET (Safe recommended) ── USDG.approve(vault, budget) ← the budget is the most that can ever be at risk
│ ▲
│ vault pulls the │ fees, trader losses, reserves back on close (reserve − trader profit)
│ reserve in the same │
▼ tx the trade opens │
PerpVault ESCROW = trader collateral + treasury reserve for each open position
│
└── trader payout on close: collateral ± PnL (profit capped at that position's reserve)
- "Send funds from my wallet when someone opens" happens automatically and atomically: the vault calls
transferFrom(treasury, vault, reserve)inside the keeper's execution. Nobody sends anything by hand. A bot holding the custody key would be the easiest thing to steal. - The vault only ever holds escrow for open positions.
usdg.balanceOf(vault) == totalCollateral + totalReservedis enforced and fuzzed.vault.backing()exposes it publicly: every USDG a trader can claim is sitting in the vault. - The reserve is
min(market reserve % × notional, max profit % × trader collateral)(900% on majors, 100% on coins). It is what the treasury commits for each position, and the most a trader can win on it (PARAMS.md).
Breach model#
| Compromised | Worst case |
|---|---|
| Custody wallet | Nothing Bias-related; it is outside the system. |
| Treasury wallet | Its USDG balance. Keep only the budget there. |
| Vault / router bug | Escrow for open positions + remaining allowance. Set the allowance to the budget, never unlimited. |
| Executor key | Can execute or refund queued requests at the oracle price; cannot set prices or move funds. Out-of-gas griefing reverts instead of refunding. Guardian removes it. |
| One signer key | Cannot move the price: with 3 signers live the median ignores it, and with only 2 fresh prices they must agree within the spread or the price is treated as stale (opens and liquidations pause, closes keep working). Guardian removes it. |
| TWAP recorder key | Can record at chosen moments, at most every 15s, so one record weighs at most 1/120 of a 30-minute TWAP; cannot wipe history. Guardian removes it. |
| Guardian key | Can make markets close-only and remove keepers and signers (a denial of service on opens). Closes keep working even with every signer removed (priced from the TWAP as stale). The Safe rotates it. |
| Owner = timelock | Every change waits 48h in public; the Safe cancels anything unexpected. |
| Deployer key after handoff | Nothing. |
Controls#
- Kill switch: from the treasury wallet,
USDG.approve(vault, 0). No new positions can open (IncreaseFailed(TreasuryShort)); every open position can still close or be liquidated, paid entirely from escrow; reserves return as positions unwind. - Close-only: the guardian sets it per market or globally, instantly. Only the timelock lifts it.
- Payout breaker: when traders net more than the per-market hourly cap, opens pause for one hour by themselves
(
vault.breakerUntil(market)); the watchdog alerts, and the guardian can escalate to close-only if the trip is real. - Daily budget (coins): when the treasury has paid the coin group $3,000 over the current hour and the previous 24,
every coin market refuses opens until older payouts roll out (
riskManager.groupUsed(2)). With the $7,500 group reserve cap, no 24h window can cost the treasury $10,500 or more across all coin markets (RISK.md 8.4). Majors have their own group and budget. - Tiers (coins): per-wallet cap, per-side cap and leverage follow the pool's conservative depth; below $14k a coin is
close-only (
riskManager.status(market)). - Delist and settle: the guardian delists a coin through the factory (
factory.delist(market), orriskManager.delist(market)for governance coins): opens stop at once, and 24h later anyone can close the remaining positions withvault.settle. A coin below $14k of depth for 3 days is settleable without anyone delisting it. - Listing: anyone can list a coin through
MarketFactory.list(poolKey)for a 100 USDG fee (at most 20 active auto markets). The guardian can pause listing, deny tokens and delist; only the timelock changes quotes, hooks, the template, the fee and the cap. - Budget:
vault.treasuryAvailable()= min(balance, allowance) is what new opens can draw.vault.setCaps(maxProfitBps, maxTotalOi)sets the aggregate OI cap (timelocked). - Move the treasury:
vault.setTreasury(newWallet)(timelocked). Escrow already held stays in the vault until positions close; payouts then go to the new wallet.
Pons launch#
- Launch $BIAS on Pons with
creatorTaxBps = 500andcreatorFeeRecipient = custody wallet. Pons applies the creator tax on buys and sells (curve:spent × bps/grossQuoteOut × bps; MemeHookafterSwap), in ETH, credited to the Pons fee escrow0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e. Claim withclaim(). Cap is 10% (maxCreatorTaxBps == 1000); curve fee + creator tax ≤ 20%. - Convert claimed ETH to USDG in the ETH/USDG Uniswap v4 pool (1 bp tier, hookless, currently the deepest).
- The $BIAS perp market can only be listed after graduation, since
UniV4TwapSourceneeds the graduated pool(ETH, token, fee 0, tickSpacing 200, MemeHook). The 5% tax on both legs makes a Pons token expensive to pump (RISK.md§3); untaxed coins rely on tiers and the daily budget instead (section 8).
Small-size launch#
Positions stay small while the treasury is funded by the $BIAS tax. script/Configure.s.sol sets a fixed per-side OI
cap and net-skew cap on every market (majors $10k / $7.5k, index $7.5k / $5k, stocks $5k / $3k, coins $5k / $5k) and
a $60k aggregate cap. Coins are further limited by liquidity tiers (Lite $750 per side up to Large $5k), a $300 to $2,000
per-wallet cap, 100% max profit and the coin group's daily budget. Majors get a $5k per-wallet cap. Raise caps through the
timelock in steps as the treasury grows (PARAMS.md, RISK.md section 8).
Listing coins#
Two paths:
- Permissionless (
MarketFactory.list). On-chain checks only: quote in native ETH, WETH or USDG; hook none or allowlisted (Pons MemeHook); static fee; token contract with sanedecimals()/totalSupply(); not denylisted; one market per coin; fee paid. The market is keeper-optional (Lite caps without signers) and refuses opens until it has 1h of records and $14k of conservative depth. Off-chain policy decides what keepers record and price: 24h volume >= min(50% of liquidity, $50k), pool age >= 12h, a honeypot sell test, no tokenized stocks / stablecoins / wrapped assets. A listing that fails the policy is not recorded, so it never opens; the guardian delists it and can deny the token. - Governance (Safe + timelock, the rule below). For coins that should start above Lite and require signers.
Listing rule for governance TWAP markets#
A token gets a governance TWAP market only when all of these hold. Record the answers in the Safe proposal that lists it.
- Pool shape. The Uniswap v4 pool is quoted in native ETH, WETH or USDG and its hook is none or the Pons MemeHook
0xE5e7…e044. Any other hook: do not list (unmodelled fees or taxes).DeployTwapSource.s.solandConfigure.s.solrefuse other pools. - Dominant venue. On DexScreener (
https://dexscreener.com/robinhood/<token>) and GeckoTerminal (https://www.geckoterminal.com/robinhood/tokens/<token>), this v4 pool holds more than half of the token's liquidity and more than half of its 24h volume across all venues (other v4 pools, v3 forks, CEX listings). If the token trades mostly elsewhere, the v4 pool is a thin, cheap-to-move price: do not list. - Real depth. In
skew-site/content/chain-tokens.json(fromscripts/scan-tokens.mjs) the pool is not flaggedconcentrated. The live tier follows the conservative depth; a concentrated pool reads deeper than it is, so give it a tier ceiling (riskManager.setMarketRisk(id, COIN, 0, ceiling)) matching its tick-walked depth. - Friction. Not needed for the caps any more: tiers and the daily budget bound losses on untaxed pools too (RISK.md 8.1). Record the fee and any tax anyway.
- Token. No extra transfer tax, rebasing, blacklist, owner pause or owner-appointed minters beyond the pool hook;
not an upgradeable proxy you do not trust. Check with
cast selectors $(cast code <token> --rpc-url $RPC_URL) --resolve(NOTE failed this on 2026-09-15:pause(),setMinter()). - Records. The
UniV4TwapSourcehas recorded for at least 1 hour, andtwap(3600)is within 1% of the pool spot and of the keeper signers' price.
Deploy#
See RUNBOOK.md. Steps 3–4 deploy and configure; step 7 hands ownership to the timelock.