Pre-launch. The protocol is not publicly live yet. This documentation describes the contracts as implemented; deployed addresses are published at launch.

Protocol Architecture

LP24 is a set of Vyper 0.4.3 contracts that manage the full token lifecycle. Tokens, bonding curves and CTO votes are deployed as minimal proxies (EIP-1167); everything else is a singleton.

Contract Overview

ContractPatternChainsRole
FactorySingletonAllEntry point. Deploys the protocol pair once, then user token + curve pairs via CREATE2 with vanity enforcement. Owns the deployer-token registry and the curve registry.
TokenTemplateMinimal proxyAllERC-20 with minter-only mint/burn, transfers locked until graduation, 9 decimals.
BondingCurveTemplateMinimal proxyAllCurve trading, pricing, graduation trigger, refund system with claim window and sweep.
PrimaryHookSingleton (V4 hook)V4 familyPrimary native↔token pool: graduation, swap fees, distribution, reinvestment, buy-and-burn, CTO.
SecondaryHookSingleton (V4 hook)V4 familyToken↔$LP24 pool per graduated token. Flat 60 bps (0.6%) fee taken in the token and burned.
GraduationManagerSingletonGnosisV3 equivalent of PrimaryHook. Manual fee collection, adaptive DEEP/THIN distribution, CTO. No secondary pool.
CTOVoteTemplateMinimal proxyAllCommunity Takeover voting. Cloned per vote by the hook or manager.
TokenSocialsSingletonAllOn-chain metadata registry. Packed bytes32 storage, owner tracked per token.
TokenVestingSingletonAllOwnerless release of the protocol token's initial mint. 24 weeks cliff, then 240,000 × 10⁹ (240k tokens) per week.
Two venue families

V4 family — Base, Polygon and Robinhood on Uniswap V4; BSC on PancakeSwap Infinity. These run PrimaryHook + SecondaryHook and get two pools per token. The Infinity port differs only in the integration layer: a six-field PoolKey with hooks third and tick spacing folded into parameters, settlement against the Vault rather than the pool manager, different callback selectors, and getHooksRegistrationBitmap() instead of address-mined permission bits.

V3 family — Gnosis on Uniswap V3, running GraduationManager. V3 has no hooks, so fees must be collected explicitly and the split differs (40 / 20 / 40 rather than 30 / 10 / 60).

Deployment

User → Factory.deploy_pair(name, symbol, salt, packed_socials)  {value: deployment_fee}
         ├─→ validate anchor block + blockhash prefix, within INCLUSION_WINDOW
         ├─→ effective_salt = keccak256(salt ‖ msg.sender)   // binds salt to caller
         ├─→ create_minimal_proxy_to(token_master, effective_salt)  → token
         ├─→ assert first byte == 0x24                        // vanity
         ├─→ create_minimal_proxy_to(bonding_curve_master)     → curve
         ├─→ is_curve[curve] = True
         ├─→ TokenTemplate.initialize(name, symbol, curve, msg.sender, msg.sender)
         ├─→ BondingCurve.initialize(token, msg.sender, False)  {value: msg.value}
         ├─→ TokenSocials.register_token(token, msg.sender, packed_socials)
         └─→ tokensByDeployer[msg.sender].append(token)

Buy on the Bonding Curve

User → BondingCurve.buy(token_lots)  {value: total}
         ├─→ balanceOf(user) / LOT_SIZE + lots <= MAX_USER_LOTS
         ├─→ _calculate_price(supply, lots, True)     // quadratic + linear + tax
         ├─→ assert msg.value == total                // exact payment
         ├─→ TokenTemplate.mint(user, lots * LOT_SIZE)
         └─→ if new_supply == MAX_SUPPLY_LOTS:
              └─→ _graduate(fees)

Graduation — V4 family

BondingCurve._graduate(fees)
  ├─→ protocol_share = fees // 10; deployer_fee = fees - protocol_share
  ├─→ raw_call(deployer, deployer_fee, revert_on_failure=False)
  │      // best-effort: a reverting deployer wallet forfeits to the hook
  ├─→ TokenTemplate.mint(PRIMARY_HOOK, GRADUATION_TOKENS)
  ├─→ TokenTemplate.enableTrading()
  └─→ PrimaryHook.createPositionAndRegister(token, deployer)  {value: hook_native}
        ├─→ assert Factory.is_curve(msg.sender)     // only a factory-born curve
        ├─→ approve PERMIT2 → POSITION_MANAGER
        ├─→ _initializing = True                    // transient init gate
        ├─→ PositionManager.multicall([initializePool, modifyLiquidities])
        ├─→ _initializing = False
        ├─→ excess beyond GRADUATION_NATIVE → protocolBalance
        ├─→ tokenInfo[token] = {deployer, positionId}; poolIdOf[token] = id
        └─→ if token != PROTOCOL_TOKEN:
             ├─→ IERC20(token).transfer(SECONDARY_HOOK, SECONDARY_TOKEN_RAW)
             └─→ SECONDARY_HOOK.notifyReserved(token) {value: SECONDARY_NATIVE_RAW}

Graduation — Gnosis (V3)

BondingCurve.initialize(...)
  └─→ GraduationManager.preInitializePool(token)
        // pool initialized atomically with deploy_pair; a frontrunner who
        // pre-inits at a bad price reverts the whole deployment tx

BondingCurve._graduate(fees)
  └─→ GraduationManager.createPositionAndRegister(token, deployer)  {value}
        ├─→ wrap native → WNATIVE
        ├─→ assert actual slot0 sqrtPrice == expected  // defense in depth
        ├─→ NonfungiblePositionManager.mint(full range, 1% fee tier)
        └─→ excess + residual WNATIVE → protocolBalance

Post-Graduation Swap

V4 family:
User → Router.execute(swap)
         └─→ PoolManager calls PrimaryHook:
              ├─→ beforeSwap()  // tax buys: take native fee
              └─→ afterSwap()   // tax sells: take native fee
              // fee → pendingFees[token], or protocolBalance for $LP24

Gnosis:
User → SwapRouter.exactInputSingle(...)   // fees accrue inside the V3 position
Anyone → GraduationManager.collectFees(token)
         ├─→ native side → pendingFees[token] (or protocolBalance for $LP24)
         └─→ token side  → pendingTokens[token]   // never sold

Design Principles

No admin keys

No owner, no admin, no multisig, no upgrade path. Once deployed the contracts are immutable. The only privileged role is deployer, which receives a fee share — and that can be transferred by community vote.

Nothing third-party can block

Paths that touch untrusted addresses are best-effort. The graduation payout to the deployer, the CTO payout to an outgoing deployer, and the registry mirror on moveDeployerToken all use revert_on_failure=False, so a contract wallet that reverts on receive cannot brick graduation or a takeover. Failed deployer payouts roll back into deployerBalance for later withdrawal.

Pool initialization gates

Both V4 hooks implement beforeInitialize and reject any initialization unless a transient _initializing flag is set — which only their own creation path sets, and which is cleared immediately after the multicall rather than left to expire at end of transaction. On Gnosis the equivalent guarantee comes from initializing the V3 pool atomically inside deploy_pair.

Gas optimization

  • No per-user storage on the bonding curve — holdings are read via balanceOf()
  • poolIdOf[token] caches the pool id, read on every swap callback
  • Transient storage (EIP-1153) for the internal-swap and pool-init flags
  • Standalone pendingFees mapping instead of a struct field
  • Deployer enumeration appends in place — O(1) regardless of how many tokens an address holds

Minimal proxy pattern

Tokens, bonding curves and CTO vote contracts are EIP-1167 minimal proxies delegating to a shared master. Deployment costs roughly 45k gas instead of ~2M.