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

Claim Deployer Fees

Withdraw your accrued share of post-graduation swap fees. Two steps: someone distributes, then you withdraw.

Why it is two calls

Swap fees accumulate in pendingFees[token] undivided. Nothing is credited to you until distributeFees(token) runs and splits them. That call is permissionless — anyone can trigger it, including you — but it is not automatic, so a token with plenty of trading volume can still show a zero deployer balance simply because nobody has distributed recently.

const HOOK_ABI = [
  "function distributeFees(address token)",
  "function withdrawDeployerFees(address token)",
  "function deployerBalance(address token) view returns (uint256)",
  "function getPendingFees(address token) view returns (uint256)",
  "function canDistribute(address token) view returns (bool)",
  "function getTokenInfo(address token) view returns (address deployer, uint256 positionId)"
];

const hook = new ethers.Contract(HOOK_ADDRESS, HOOK_ABI, signer);
from web3 import Web3

HOOK_ABI = json.load(open("PrimaryHook.abi.json"))

w3 = Web3(Web3.HTTPProvider(RPC_URL))
acct = w3.eth.account.from_key(PRIVATE_KEY)
hook = w3.eth.contract(address=HOOK_ADDRESS, abi=HOOK_ABI)

def send(fn):
    """Build, sign and broadcast — used throughout these examples."""
    tx = fn.build_transaction({
        "from": acct.address,
        "nonce": w3.eth.get_transaction_count(acct.address),
    })
    signed = acct.sign_transaction(tx)
    return w3.eth.wait_for_transaction_receipt(
        w3.eth.send_raw_transaction(signed.raw_transaction)
    )

Step 1 — Distribute

if (await hook.canDistribute(token)) {
    await (await hook.distributeFees(token)).wait();
}
if hook.functions.canDistribute(token).call():
    send(hook.functions.distributeFees(token))

On Gnosis there is an extra step first — V3 fees sit inside the position until pulled out:

// Gnosis only
await (await manager.collectFees(token)).wait();
await (await manager.distributeFees(token)).wait();
# Gnosis only
send(manager.functions.collectFees(token))
send(manager.functions.distributeFees(token))

Step 2 — Withdraw

const balance = await hook.deployerBalance(token);
if (balance > 0n) {
    await (await hook.withdrawDeployerFees(token)).wait();
}
balance = hook.functions.deployerBalance(token).call()
if balance > 0:
    send(hook.functions.withdrawDeployerFees(token))

The withdrawal pays out the full balance; there is no partial withdrawal. Only the address currently registered as deployer can call it.

Your share

Chain familyDeployerProtocolReinvested
V4 family30%10%60%
Gnosis40%20%40%
On Gnosis your cut is native-side only

V3 pays fees in both currencies. The deployer and protocol shares come exclusively from the native side; token-side fees go into a separate bucket and are burned or paired back into liquidity, never sold. Your 40% is 40% of the native fees, not of total fee value.

Distribution may be partial

The reinvest swap is bounded by the MEV cap, so a single distributeFees call may process only part of the pending balance. The rest carries forward and is picked up next time. This is normal — it is not a failure, and repeated calls will drain the backlog. A large accumulated balance on a thin pool may need several calls.

Checking a token's position

const [pending, credited, info] = await Promise.all([
    hook.getPendingFees(token),   // undistributed
    hook.deployerBalance(token),  // credited, withdrawable
    hook.getTokenInfo(token)      // (deployer, positionId)
]);

// Your eventual cut of `pending` is 30% (40% on Gnosis)
pending  = hook.functions.getPendingFees(token).call()    # undistributed
credited = hook.functions.deployerBalance(token).call()   # withdrawable
info     = hook.functions.getTokenInfo(token).call()      # (deployer, positionId)

# Your eventual cut of `pending` is 30% (40% on Gnosis)
After a CTO, the role has moved

TokenTemplate.deployer() records the original launcher and is never updated. The authoritative current deployer — the one who can withdraw — is getTokenInfo(token).deployer on the hook. If a takeover succeeded, any balance you had not yet withdrawn was paid out to you during finalization; if that payment failed, it rolled into the balance the new team now owns.

Common reverts

RevertCause
Not deployerCaller is not the currently registered deployer
No feesNothing credited — distribute first
No deployer fees for protocol token$LP24 has no deployer share
No positionToken has not graduated
Pool too thin for any safe swapPool cannot absorb even a capped swap — wait for liquidity