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

PrimaryHook

The post-graduation manager for the native↔token pool on V4-family chains. Implements the swap callbacks, owns the LP position, and provides permissionless fee distribution, reinvestment, buy-and-burn and CTO voting.

Contract Type

Singleton — one per chain, on Base, BSC, Polygon and Robinhood. Uniswap V4 hook (PancakeSwap Infinity port on BSC). No owner, no admin keys. Gnosis uses GraduationManager instead.

Graduation

createPositionAndRegister(token: address, deployer: address) -> uint256 external payable

Callable only by a factory-registered bonding curve. Initializes the pool, mints the full-range position, registers the token, and pushes the secondary envelope. Returns the position id.

$LP24 takes the full envelope with no split. Every other token mints 90% into the primary pool and pushes 10% to the SecondaryHook in the same transaction.

Reverts
  • "Only a factory curve may graduate"Factory.is_curve(msg.sender) is false
  • "Already registered"
  • "Invalid deployer"
  • "Insufficient graduation funds"msg.value < GRADUATION_NATIVE
  • "Secondary native push failed"
Emits

TokenRegistered(token, deployer, positionId), then SecondaryReserved(token, deployer) for non-protocol tokens

creditProtocolBalance() external payable

Accept a protocol share. Called by bonding curves on refund initiation and on sweep. Adds msg.value to protocolBalance.

Fee Distribution

distributeFees(token: address) external

Split pending fees 30% deployer, 10% protocol, 60% reinvested. Anyone can call. Not for $LP24.

Deployer and protocol shares are credited and pendingFees zeroed before the reinvest swap. If the MEV cap prevents the full reinvest amount being processed, the remainder is added to protocolBalance rather than back to pendingFees — it has already been split and must not be taxed twice.

Reverts
  • "Use processProtocolFees" — token is $LP24
  • "No position" — token not registered
  • "No fees"pendingFees is zero
Emits

FeesDistributed(token, total, reinvested), then Reinvested(...) or BuyAndBurn(...)

processProtocolFees() external

Process the accumulated protocol balance against the $LP24 pool. Anyone can call. Buys and burns $LP24 when the pool holds ≥ 24,000,000 tokens; reinvests to deepen the pool when it holds fewer.

Only the MEV-capped chunk is processed per call; the remainder rolls forward.

Reverts
  • "Nothing to process" — balance at or below 1,000,000 wei
  • "No protocol token"
  • "Pool not initialized"
  • "Pool too thin for any safe swap"
  • "No position" — on the reinvest branch
Emits

BuyAndBurn(token, nativeSpent) or Reinvested(...)

withdrawDeployerFees(token: address) external

Deployer only. Withdraws the full accrued balance for that token. After a successful CTO this is the new deployer.

Reverts
  • "No deployer fees for protocol token"
  • "Not deployer"
  • "No fees"
Emits

DeployerWithdrawal(token, deployer, amount)

CTO

initiate_cto_vote(token: address) -> address external payable

Clone a vote contract and transfer the initiator's stake into it. Returns the vote address. Requires the exact chain fee and 60,000,000 × 10⁹ (60M, 5% of supply) approved to this contract.

Reverts
  • "CTO not enabled"
  • "Protocol token cannot be CTO'd"
  • "Send exact CTO fee" — exact, no overage refund
  • "Token not registered"
  • "CTO vote already active"
  • "Stake transfer failed"
Emits

CTOVoteInitiated(token, vote_contract, initiator)

finalize_cto(token: address, new_deployer: address) external

Vote contract only. Transfers the deployer role, mirrors the change into the factory registry, pays out the outgoing deployer's balance (best effort), and transfers socials ownership.

Reverts
  • "No active vote"
  • "Not vote contract"
  • "Invalid new deployer" — zero address would lock fees permanently
Emits

DeployerUpdated(token, oldDeployer, newDeployer, paidOut)

cancel_failed_cto(token: address) external

Clear a failed vote so a new one can be initiated. Anyone can call.

Reverts
  • "No active vote"
  • "Vote still active"
  • "Vote was finalized"
  • "Vote passed - must finalize"

Hook Callbacks

Called by the pool manager. Not for direct use; all three revert for any other caller.

CallbackBehaviour
beforeSwap(sender, key, params, hookData)Taxes buys on the input side. Asserts amountSpecified < 0 — exact-output buys are disabled. Skipped for internal swaps.
afterSwap(sender, key, params, swapDelta, hookData)Taxes sells on the native output delta. Skipped for internal swaps.
beforeInitialize(sender, key, sqrtPriceX96)Reverts "Pool init not permitted" unless the transient _initializing flag is set — which only createPositionAndRegister sets, and clears immediately after.

View Functions

FunctionReturns
getTokenInfo(token) -> TokenInfo(deployer, positionId) — the authoritative current deployer
getPendingFees(token) -> uint256Undistributed native fees
getFeeInfo(token) -> (uint256, uint256)(currentFeeBps, nativeInPool), computed live
isTokenRegistered(token) -> boolWhether the token graduated through this hook
canDistribute(token) -> boolPending fees, has a position, and is not $LP24
canProcessProtocolFees() -> boolProtocol balance above dust and $LP24 is set
getPoolLiquidity(token) -> (uint128, uint160, int24)(liquidity, sqrtPriceX96, tick)
getPoolId(token) -> bytes32Pool id for the primary pool
canInitiateCTO(token) -> boolRegistered, no active vote, template configured
getHookPermissions() -> uint2568396 (0x20CC). On BSC this is getHooksRegistrationBitmap() returning 0x0CC1.
protocolBalance() -> uint256Accumulated protocol share
deployerBalance(token) -> uint256Withdrawable deployer balance
pendingFees(token) -> uint256Public mapping accessor
poolIdOf(token) -> bytes32Cached pool id, populated at registration
active_cto_votes(token) -> addressCurrent vote contract, or zero
cto_vote_count(token) -> uint256Attempts to date — informational, uncapped

Constants

ConstantBaseBSCPolygonRobinhood
FEE_TIER_1_BPS120 bps (1.2%)
FEE_TIER_2_BPS80 bps (0.8%)
FEE_TIER_3_BPS40 bps (0.4%)
FEE_THRESHOLD_TIER_212 ETH36 BNB288,000 POL12 ETH
FEE_THRESHOLD_TIER_324 ETH72 BNB576,000 POL24 ETH
GRADUATION_NATIVE4 ETH12 BNB96,000 POL4 ETH
PRIMARY_NATIVE_RAW3.6 ETH10.8 BNB86,400 POL3.6 ETH
SECONDARY_NATIVE_RAW0.4 ETH1.2 BNB9,600 POL0.4 ETH
CTO_VOTE_FEE0.01 ETH0.03 BNB240 POL0.01 ETH
INITIAL_SQRT_PRICE250541448375047931186413801561446501726624926496477173928716172380951520765437783147325054144837504793118641380156
GRADUATION_LIQUIDITY126491106406735173221908902300206644531959591794226542478551264911064067351732
PRIMARY_GRADUATION_LIQUIDITY113841995766061655819718012070185980071763632614803888230691138419957660616558
GRADUATION_TOKENS400,000,000 × 10⁹ (400M)
PRIMARY_TOKEN_RAW360,000,000 × 10⁹ (360M)
SECONDARY_TOKEN_RAW40,000,000 × 10⁹ (40M)
CTO_INITIATOR_STAKE60,000,000 × 10⁹ (60M, 5% of supply)
MAX_REINVEST_SLIPPAGE_BPS480 bps (4.8%)
CAP_MULT_BPS100 (1.00× the live fee tier)
MIN_TOKEN_DUST1,000,000
TICK_SPACING200
POOL_FEE0 — fees taken by the hook