Infection routes
How a virus is reached from ETH one hop at a time, which venue serves each hop, and the approvals and slippage each hop needs.
§Why a virus needs a route
A virus has no ETH market. Its Pons curve accepts only its host, and its graduated pool pairs it only with its host. To buy a virus with ETH you walk down the infection, one market per hop:
ETH → root host → … → virus
For the example $DOG → $FLU (illustrative tickers), the route is ETH → $DOG → $FLU: buy $DOG with ETH on $DOG's venue, then buy $FLU with $DOG on $FLU's venue. For a depth-3 virus the route has four hops.
§How the route is resolved
The route resolver (packages/chain/src/route.ts) starts at the target and follows Pons pair links upward:
- Read
factory.getLaunchedToken(target). If no Pons launch record exists, the target is not a Pons V2 launch token and there is no route. - Record the launch. If its
pairTokenis the zero address, the root is ETH. Stop. - Read the launch record of the
pairToken. If it does not exist, the pair is a non-Pons asset: the root is external. Stop. - Otherwise move up to the
pairTokenand repeat.
The walk is bounded to MAX_DEPTH + 1 launches. A longer chain raises RouteTooDeepError, which cannot happen for a registered virus because VIRUS never registers a virus deeper than 3.
The result is an ordered list of nodes (ETH or an external root asset, then each token down to the target) and legs, one per hop. Each leg records its Pons launch, its curve, and, for graduated launches, the V4 pool key and pool id.
§Venues
Each leg is served by exactly one venue, chosen from the Pons graduation phase of the token being bought on that leg:
| Venue | When | How the hop is crossed |
|---|---|---|
curve |
Phase NotGraduated and the curve is not sold out |
PonsV2BondingCurve.buy / sell, called directly |
migration |
Phase NotGraduated with readyToGraduate() == true, or phase Swept |
No trading. Someone must first run COMPLETE MIGRATION (graduate, then createGraduatedPool). Both are permissionless. |
pool |
Phase PoolCreated |
Uniswap V4 pool with the Pons meme hook, through the Uniswap Universal Router (V4_SWAP) and Permit2 |
rescued |
Phase Rescued |
None. The Pons owner released the swept reserves; there is no market. |
external |
The root host is paired with a non-Pons asset | Acquire that asset outside VIRUS. The route starts from it. |
A route is complete only when every leg is curve or pool. If any leg is migration, the app offers COMPLETE MIGRATION for that launch first. If any leg is rescued, the route is blocked at that hop.
Quotes differ by venue. Curve legs are quoted locally with a port of the Pons curve math over live reserves (see Trading). Pool legs are quoted by simulating V4Quoter.quoteExactInputSingle.
§One signed transaction per hop
A multi-hop buy is never bundled. The app signs one hop at a time, with explicit step counters such as APPROVE HOST 1/3, and each step shows its own live quote, slippage setting, minimum output, approval state, transaction status and explorer link.
This has two consequences:
- Each hop is quoted live when you sign it. Prices can move between hops.
- You hold the intermediate token between hops. If you stop after hop 1 you hold the host, not the virus. Nothing is reverted automatically.
Selling out to ETH walks the same legs in reverse, one transaction per hop: sell the virus for its host, then sell the host for its own pair asset, and so on up to ETH.
§Approvals
Robinhood Chain is an EVM chain, so an ERC-20 must be approved before a contract can move it. Native ETH never needs an approval.
| Hop | Input | Approvals |
|---|---|---|
| Curve buy with ETH (root host paired with ETH) | ETH sent as msg.value |
None |
| Curve buy with a host token | Host (ERC-20) | host.approve(curve, amount). The app approves the exact amount by default. |
| Curve sell | The token being sold | token.approve(curve, amount) |
| Pool swap with ETH in | ETH sent as msg.value |
None |
| Pool swap with an ERC-20 in | Host or virus | The token must be approved to Permit2, then Permit2 must grant the Universal Router an allowance: Permit2.approve(token, router, amount, expiration) with signature approve(address,address,uint160,uint48). |
| Inoculate with a first buy | Host | host.approve(coordinator, firstBuy + inoculationFee) |
The coordinator resets its own curve approval to zero after the first buy, and the feeder never grants any allowance.
§Graduated-pool swaps
Pool legs are encoded as a single Universal Router execute(commands, inputs, deadline) call with command V4_SWAP (0x10) and actions SWAP_EXACT_IN_SINGLE (0x06), SETTLE_ALL (0x0c), TAKE_ALL (0x0f). The ExactInputSingleParams struct VIRUS encodes, taken from Uniswap's published interface, carries a minHopPriceX36 field; a secondary source says Robinhood Chain's router structs differ from other chains', consistent with that field. VIRUS sets minHopPriceX36 to zero and uses amountOutMinimum as the bound, which is never zero.
The swap encoding is UNVERIFIED against the deployed router. 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
Every hop has its own slippage setting:
| Option | Tolerance |
|---|---|
| 0.5% | 50 bps |
| 1% (default) | 100 bps |
| 3% | 300 bps |
The minimum output is computed from the live quote:
minOut = floor(quote × (10,000 − slippageBps) / 10,000)
VIRUS never submits a production trade with a zero minimum. If there is no live quote, or the computed minimum rounds to zero, the app refuses to build the transaction (ZeroMinimumError). The coordinator enforces the same rule on chain for the first buy: firstBuy > 0 with minVirusOut == 0 reverts with ZeroSlippageBound.
§Depth limit
A route has at most four hops: ETH (or an external root) to the root host, then up to three VIRUS layers. The limit comes from MAX_DEPTH = 3 in VirusEconomics, enforced at launch by hostCheck and registerVirus.
§Costs along a route
Each hop is a separate trade on a separate Pons launch and pays that launch's fees, in that hop's input or output asset:
- Buying the root host with ETH pays the root host's Pons fees. If the root host is not itself a virus, none of that fee reaches a VIRUS feeder.
- Buying a virus with its host pays the virus's fees in the host. That fee feeds the virus's feeder, which burns host.
A deep route therefore pays fees several times, plus price impact on every market it crosses. The app shows each hop's fees, expected output and minimum before you sign it. See Risks for thin-liquidity and nested-route risks.