← Back

V9: Dynamic-Fee Continuous Curves, Aggregator-Native Routing


V9: Dynamic-Fee Continuous Curves, Aggregator-Native Routing

Trinity's hook architecture went through eight on-chain iterations before V9. Each one solved a problem the previous version exposed. V9 doesn't change what the curve does — it changes how the rest of the ecosystem sees it. The single continuous concentrated liquidity position from V8 is preserved exactly. What changes is two interface decisions that, together, make the difference between "you launched a thing nobody can route to" and "Farcaster's native swap widget quotes against you within hours of deployment."

The Two Things V8 Got Wrong (Routing-Wise)

V8 worked. Tokens launched on it. Buy fees flowed to treasury, sell fees burned. The curve was clean. But routers like 1inch, KyberSwap, 0x, OpenOcean — the aggregators that downstream wallets and mini-apps actually use to find swaps — silently skipped the pools. Two reasons:

1. The fee field on the PoolKey was 0. V4's "static fee" mode lets a pool advertise its LP fee on-chain. V8 set this to 0 and then took its actual fee via a BeforeSwapDelta skim inside the hook. From the aggregator's perspective, that looks like a "free pool that mysteriously delivers less than quoted." Quote engines blacklist pools that quote inconsistently — so they wrote V8 pools out of their graphs.

2. beforeAddLiquidity reverted for any non-self caller. V8 wanted to keep curve depth fully under deployer control. So it blocked all external modifyLiquidity calls. The problem: aggregators like 0x and 1inch routinely simulate modifyLiquidity as a honeypot probe before quoting against a pool. Our hard-revert looked exactly like a honeypot. Aggregators skipped.

Both decisions were correct in spirit (predictable depth, no LP fragmentation) and wrong in mechanics (no aggregator picked us up).

What V9 Changes

Dynamic LP fee. Pools now deploy with key.fee = LPFeeLibrary.DYNAMIC_FEE_FLAG (0x800000). Immediately after PoolManager.initialize fires, the hook's afterInitialize callback calls manager.updateDynamicLPFee(key, feeBps × 100) — broadcasting the real fee on-chain in pips. Aggregators read this the same way they read fees on any standard V4 pool. The fee is enforced via standard V4 LP-fee accounting; the hook claims accrued fees in afterSwap via a zero-delta modifyLiquidity call and routes them by currency (quote-side → feeRecipient, token-side → 0x…dEaD). The economic outcome is identical to V8 (buy fees fund the project, sell fees burn the token), but the mechanics are vocabulary that every router already speaks.

LP allowlist instead of permanent block. beforeAddLiquidity now allows the hook itself OR any address in an owner-mutable lpAllowed mapping. The deployer seeds the allowlist with known router and PositionManager addresses on Base — Uniswap V4 PositionManager, Universal Router, Permit2, 1inch v6, KyberSwap, OpenOcean. Their honeypot probes succeed. The pool stays in their quote graphs. Curve depth remains controlled (no random LPs can mint into the position) because random addresses are still blocked by default, but the surface for aggregator simulation is open.

Permission bits change accordingly. V9 hooks expose AFTER_INITIALIZE | BEFORE_ADD_LIQUIDITY | AFTER_SWAP (mining target 0x1840). The BEFORE_SWAP and BEFORE_SWAP_RETURNS_DELTA bits used by V8's skim are gone — the fee logic moved entirely to the standard V4 LP-fee path.

Everything else is V8: single continuous position spanning the configured tick range, no bands, no transitions, no afterSwap rebalancing of liquidity. The position math is unchanged. The deflationary mechanic is unchanged. Only the surface presented to the rest of the ecosystem changed.

The Concrete Outcome

Within 24 hours of migrating $EPIC's pools to V9, Farcaster's native swap widget started quoting against them. No listing applied for. No integration written. Aggregators discovered the pools through their normal V4 indexing pipeline, saw a real fee number, simulated modifyLiquidity and got a successful response, included the pools in their quote graphs. The Trinity launcher backbone now produces tokens that are tradeable in Farcaster, Coinbase Wallet, and any aggregator-backed mini-app on day one.

$TRINI's pools followed the same migration path and are at various stages of indexer pickup. Future projects launched on the backbone deploy directly into V9 — no V8 phase, no migration cost.

What's Live

The current V9 deployments on Base:

$EPIC (5 pools, all V9, all routing through aggregators):

  • USDC, WETH, cbBTC, CLANKER, TRINI

$TRINI (3 pools, all V9, indexer pickup in progress):

  • USDC, WETH, CLANKER

$WIP (4 pools, V9 hooks deployed and registered, awaiting migration from V8):

  • USDC, WETH, CLANKER, TRINI

The hook source is TrinityHookV9.sol in the Trinity Labs GitHub. All deployed instances are verified on Basescan. The token-launcher cross-pair architecture from V8 is preserved exactly — every project gets a PROJECT/TRINI cross-pair pool that captures arb flow when the project trades, channeling buy pressure back into TRINI.

Why Bother

The cost of "I built my own thing on V4 and now nobody can quote against it" was real, and we've zeroed it out. New launches on the Trinity backbone don't need their own integration team to negotiate with aggregators or build custom routing. They speak standard V4 dynamic-fee vocabulary; the routers handle the rest. The deflationary curve mechanics that V8 introduced are intact. The only thing we lost is the friction.