Trading
How virus buys and sells work on the Pons curve and in the graduated pool, how quotes and price impact are computed, and what is still unverified.
§Two trading phases
Every virus trades in two phases, both run entirely by Pons:
| Phase | Pons phase | Venue | Fee rate | Fee currency |
|---|---|---|---|---|
| Curve | NotGraduated |
PonsV2BondingCurve of the virus |
curveFeeBps + creatorTaxBps |
Always the host |
| Pool | PoolCreated |
Uniswap V4 pool of virus/host with the Pons meme hook | hookFeeBps + creatorTaxBps |
The swap's unspecified currency |
Between them sits migration: a sold-out curve waiting for graduate, then phase Swept waiting for createGraduatedPool. Nothing trades during migration. See Graduation.
VIRUS does not run a market. Trades go directly to the Pons curve or to the Uniswap Universal Router. VIRUS contracts are not in the trade path, and the only trade VIRUS contracts ever make is the atomic first buy inside inoculate.
§Buying on the curve
A buy spends host and receives virus. The fee legs are taken from the input, on the quote side (VERIFIED_PONS C2):
fee = floor(spent × feeBps / 10,000)
tax = floor(spent × creatorTaxBps / 10,000)
net = spent − fee − tax
tokensOut = getAmountOut(net, quoteReserve, tokenReserve, 0)
= net × tokenReserve / (quoteReserve + net) (floored)
feeBpsis the Pons base fee snapshotted on the curve (curve.feeBps()).creatorTaxBpsis the creator tax VIRUS set at launch (curve.creatorTaxBps()).- The fee and the tax are each floored separately.
getAmountOutis called with a zero fee because the fees were already taken.
The call is:
function buy(uint256 quoteIn, uint256 minTokensOut, address recipient)
external payable returns (uint256);
For a virus, quoteIn is in host tokens and requires a prior host.approve(curve, quoteIn). The curve credits ERC-20 input by balance delta.
§Reserves
The curve prices against a virtual reserve that includes a phantom quote amount (VERIFIED_PONS C4):
quoteReserve = phantomQuote + trackedQuote − quoteFeeBalance − creatorTaxBalance
realQuoteReserve = trackedQuote − quoteFeeBalance − creatorTaxBalance
getReserves() returns (quoteReserve, tokenReserve). For a virus, phantomQuote and the graduation threshold come from Pons's pairTokenEconomics(host), in host units. Collected fees and tax sit on the curve (quoteFeeBalance, creatorTaxBalance) until swept, and are excluded from the pricing reserve.
§Partial fill at the graduation boundary
Only supply − reservedTokens tokens can ever be sold from the curve; the rest is reserved for the graduated pool. A buy that would take more than the remaining sellable amount is partially filled up to it, and the unspent host is refunded to msg.sender (VERIFIED_PONS C6). The app's quote reproduces this: it computes the net input needed for the remaining tokens with getAmountIn, grosses it up for the fee and tax, rounding up, and reports the spent amount, the refund and a clamped flag.
§Selling on the curve
A sell spends virus and receives host. The fee legs are taken from the gross output, again on the quote side (VERIFIED_PONS C3):
gross = getAmountOut(tokensIn, tokenReserve, quoteReserve, 0)
fee = floor(gross × feeBps / 10,000)
tax = floor(gross × creatorTaxBps / 10,000)
quoteOut = gross − fee − tax
function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient)
external returns (uint256);
On the curve, every fee and every creator tax is paid in the host, on both buys and sells. That is why a virus's curve phase feeds its feeder purely in host.
§Sells close at readyToGraduate
Once the curve has sold its entire sellable allocation, readyToGraduate() becomes true (sellableTokens() == 0, VERIFIED_PONS C5). From then on sells are closed (VERIFIED_PONS C3) and there is nothing left to buy. The app disables trading on that leg and offers COMPLETE MIGRATION instead. Trading resumes in the pool once migration completes.
§Quotes
Curve quotes are computed locally by packages/chain/src/curveMath.ts, a port of PonsV2BondingCurveMath and the fee legs of PonsV2BondingCurve.buy and sell from the published Pons source, run over reserves read live from the curve. The two base formulas (VERIFIED_PONS M1, M2):
getAmountOut(in, rIn, rOut, fee) = in·(10,000 − fee)·rOut / (rIn·10,000 + in·(10,000 − fee))
getAmountIn(out, rIn, rOut, fee) = out·rIn·10,000 / ((rOut − out)·(10,000 − fee)) + 1
Pool quotes are obtained by simulating V4Quoter.quoteExactInputSingle against the live pool, which includes the Pons hook fee and creator tax.
§Spot price and price impact
- Spot price of one virus in host:
quoteReserve / tokenReserve, including the phantom reserve. - Buy price impact compares the tokens received with the tokens the full input would buy at spot:
(spotTokens − tokensOut) / spotTokens, wherespotTokens = spent × tokenReserve / quoteReserve. Becausespentincludes the fee and tax, the buy impact figure includes them. - Sell price impact compares the gross output, before fees, with the spot value of the tokens sold:
(spotValue − gross) / spotValue, wherespotValue = tokensIn × quoteReserve / tokenReserve.
Every curve trade shows its fee lines (PONS BASE FEE, CREATOR TAX), expected output and MINIMUM RECEIVED before signing. A pool swap shows the V4 quoter's expected output, which already has the hook fee and creator tax deducted, and its minimum.
§The launch-window snipe tax
The published Pons factory snapshots two values into every launch: snipeTaxStartBps (published-source default 9,900) and snipeTaxSeconds (published-source default 15, maximum 60). It auto-exempts the launch's originalDeployer and creatorFeeRecipient, and calls exemptFromSnipeTax on the curve (VERIFIED_PONS F15, factory side).
The published PonsV2BondingCurve has no snipe-tax logic and no exemptFromSnipeTax function (UNVERIFIED X1). The deployed curve must therefore differ from the published one, and VIRUS does not know:
- whether the tax is keyed on
msg.senderor onrecipient; - its exact rate over the window. A secondary source describes it as decaying to zero over 5 seconds, while the published factory default window is 15 seconds (
UNVERIFIEDX5).
What VIRUS does about it:
- The app reads
snipeTaxStartBps()andsnipeTaxSeconds()live and never displays a constant. - Quotes that fall inside a launch's snipe window are flagged, never silently assumed untaxed. The local quote port does not include the unpublished tax.
- The coordinator's first buy is exempt under either keying (see Launching).
§After graduation: the pool
Once Pons has created the graduated pool, the virus trades in a Uniswap V4 pool whose currencies are the virus and its host, whose LP fee is zero (poolFee must be 0), and whose hook is the Pons meme hook (VERIFIED_PONS F19, H7). All fees in this phase are hook fees.
The hook takes hookFeeBps + creatorTaxBps of the swap's unspecified currency (VERIFIED_PONS H2). For the exact-input swaps VIRUS builds:
| Swap | Specified (input) | Unspecified (output) | Fee is taken in |
|---|---|---|---|
| Buy virus with host | Host | Virus | Virus |
| Sell virus for host | Virus | Host | Host |
This has a direct effect on feeding. Post-graduation buys accrue fees in the virus token, and Pons lets only its fee sweep operator convert those. Until it does, the feeder reports AWAITING_PONS_SWEEP. See Feeding.
The pool's Pons protocol cut is hookFeeBps × protocolFeeShareBps, and the feeder's share is hookFeeBps × (1 − protocolFeeShareBps) + creatorTaxBps. Because hookFeeBps and curveFeeBps are separate Pons parameters, the pool-phase total can differ from the curve-phase total. The app shows the two phases as separate rows.
§Executing pool swaps
Pool swaps go through the Uniswap Universal Router with Permit2 (see Infection routes). A swap takes three signed steps:
- an exact approval of the input token to Permit2;
- a Permit2 allowance for the router;
- the swap itself, with a minimum output from the V4Quoter's quote.
The encoding matches the source of Universal Router 2.1.2, the version deployed on Robinhood Chain. In the local lab it has been executed against pinned builds of that router and its Uniswap V4 contracts. The deployed contracts on Robinhood Chain are still UNVERIFIED, so graduated-pool trading stays disabled in the production app until the fork test test_fork_UniversalRouterSwap passes. Until then VIRUS does not submit pool swaps.
§Slippage and minimums
Every trade uses one of three slippage settings, 0.5%, 1% (default) or 3%, and a minimum output of floor(quote × (10,000 − slippageBps) / 10,000). A zero minimum is never submitted. See Infection routes.
§Where the fees go
| Part of the fee | Recipient |
|---|---|
| Pons protocol share of the base or hook fee | Pons |
| Remainder of the base or hook fee, plus the full creator tax | The virus's VirusFeeder, credited in host through the Pons fee escrow |
| Of what the feeder receives | 50% burned from host supply, 30% booked to the developer, 20% to the VIRUS treasury |
The exact numbers are in Fees and burns.