For the complete documentation index, see llms.txt. This page is also available as Markdown.

Fee streaming and credits

The Kumbaya launchpad has two parallel earnings streams: trading fees (from the V3 pool) and credits (from skim + gifts in FuelVault). They behave differently before and after graduation.

Pre-graduation: trading fees

Pre-graduation fees accumulate inside the bonding-curve positions just like any V3 liquidity. They are collected by:

function claimFees(address token) external;

on FireGraduator. Anyone can call it. It pokes every active position (burns 0 liquidity to trigger fee accounting) and collects everything.

Split: pre-graduation trading fees go 100% to the protocol; the creator's trading-fee income begins at graduation. (On-chain the contract splits graduationFeeBps to the registry's treasury and the remainder to its integrator, but both resolve to the same Kumbaya protocol address, so it all lands with the protocol.) The graduationFeeBps is snapshotted at launch, stored on the LaunchState, and immutable for the rest of the bonding phase - read it with FireLaunch.getLaunchState(token).graduationFeeBps.

The creator's trading-fee income begins at graduation (via the post-graduation stream); pre-graduation trading fees flow to the protocol. Creators also earn throughout from FuelVault credits/gifts.

At graduation

graduate() collects any remaining accumulated fees and splits them with the same snapshotted graduationFeeBps. Then ownership of all liquidity transfers to a single full-range NFT held by FireStream. From this point on, fee streams shift.

Post-graduation: trading fees

function claimFees(address token) external;

on FireStream - anyone can call. The function reads the current beneficiary list from FireRegistry.getStreamingRecipients() (a global, owner-controlled list of up to 10 recipients each with a bps share), and splits accumulated fees:

total fees collected from the NFT
   ├─ recipient[0]: bps[0] / 10000
   ├─ recipient[1]: bps[1] / 10000
   ├─ ...
   └─ creator: 10000 - sum(bps) / 10000

So the creator receives whatever bps remain after the registry recipients. If the registry totals to 7000 bps (70%), creators get 30%. The split applies separately to token0 and token1, meaning creators receive both the launched token and the numeraire (e.g. WETH) proportionally.

Important properties:

  • Beneficiaries are dynamic, not snapshotted. They're read from the registry at every claimFees call. If governance updates the recipient list, the new split applies immediately to the next claim.

  • The list can be locked permanently. FireRegistry.lockStreamingRecipients() is an irreversible one-shot - after it's called, no future change is possible. Once locked, creators have a guaranteed permanent share of 10000 - totalBps of all future fees on every graduated token.

  • Max 10 protocol recipients, each with a bps share. Sum of bps cannot exceed 10000. Anything left over goes to the creator.

  • Creator address comes from FireLaunch.getLaunchState(token).creator - set at ignite time, never changed by the contracts. (FireStream also pins the creator on its own tokenInfo[token].creator field at graduation when receiveNFT(token, nftId, creator) is called, so post-grad reads can also come from FireStream.) The "Claim" UI on kumbaya.xyz is purely an off-chain metadata association - it does not change the on-chain creator.

  • No per-token override. All graduated tokens share the same registry recipient list.

FuelVault - credits, gifts, and burn

FuelVault is a credit ledger that sits alongside the trading fee path. Two distinct flows feed it.

🪙 Frontend naming note. FuelVault is exposed on kumbaya.xyz as the Tip Jar. The gift / giftWithSig calls below are the user-facing Tip action. The skim deposit on each buy is shown as a tip bonus. Contract names (FuelVault, gift, giftWithSig, Gifted, CreditsBurned, creatorBuckets) haven't changed - only the UI vocabulary has.

Skim flow - credits go to the buyer, not the creator

The FireToken ERC-20 has a transfer hook. On every transfer FROM the pool (i.e. every buy), it deducts skimBps of the transferred amount and mints that as credits to the buyer inside FuelVault. Sells, P2P transfers, and post-graduation transfers don't trigger skim (the hook is disabled at graduation; FireGraduator also flips a bypass flag during fee collection so its own transfers aren't skimmed).

So skim does not flow directly to creators. It gives buyers a credit balance they can later gift.

Gifting - how credits become creator income

The user signs an EIP-712 GiftPermit (user → creator → token → amount → deadline → nonce); the guardian submits the transaction so the user pays no gas. Internally:

  • deducts from credits[user][token]

  • increments creatorBuckets[creator][token]

  • if pre-graduation: splits into liquid and vested portions per registry.fuelVestedBps()

  • if post-graduation: 100% goes to liquid (no further locking)

  • reverts with SelfGiftNotAllowed if user == creator - buyers can't route their own skim into their own creator bucket

Direct gift() (without a signature) is not exposed publicly - all user-initiated gifting is signature-relayed via the guardian. There is also a deposit(beneficiary, token, amount) entry point that adds to a beneficiary's credits balance, used for protocol-level seeding.

Two distinct vesting concepts (don't conflate them)

Mechanism
Lives on
When it locks
When it unlocks

FuelVault vested bucket

creatorBuckets[creator][token].vested

Set on each pre-grad gift, fraction = fuelVestedBps

At graduation, on creator's next withdraw call (the function moves vestedliquid and sets unlocked = true)

FireToken linear vesting

FireToken.releasableAmount() / releaseVested()

Defined on creation by creatorAllocationBps

Linear from graduation over vestingDuration (default frontend value: 90 days). With creatorAllocationBps = 0 this is a no-op

The FuelVault "vested" bucket is binary (locked until graduation, then fully unlocked) - not a linear schedule. Linear vesting only applies to the on-token FireToken allocation, which is zero in default frontend launches.

Withdrawing

Called by the creator. Reverts with InsufficientLiquid if the liquid bucket is empty. On the first post-graduation withdrawal it also unlocks any pre-grad vested portion (moves it to liquid before paying out).

executeBurn

Burns any user credits that were never gifted into a creator bucket - those tokens are transferred to 0xdEaD. Callable by anyone after the burn countdown elapses (set at graduation: burnTime[token] = graduation_timestamp + burnCountdownDuration, configurable 1–90 days). One-shot per token.

Creator-bucket credits are not affected - they're protected by the bookkeeping. Only credits that users still hold but never gifted before the deadline are at risk.

Creator vesting (on the token itself, separate from FuelVault)

The portion of the creator allocation that didn't go into FuelVault sits on the FireToken contract under a linear vesting schedule.

⚠️ In the production frontend default, this is zero. Default launches set creatorAllocationBps = 0, which means no creator vesting schedule is set up. The vesting machinery below only matters if you call ignite() yourself with a non-zero creatorAllocationBps.

The schedule:

  • Starts at graduation (not at launch). Pre-graduation, vestedAmount() == 0 (because vestingStart == 0).

  • No cliff. Linear from vestingStart over vestingDuration seconds. The frontend default is 90 days; the registry-allowed range is [90 days, 730 days].

  • vestingDuration == 0 is a special case: everything is fully vested immediately at graduation. (Only reachable for launches with creatorAllocationBps > 0 and creatorGiftBalanceCaptureBps < 10000 if registry's requiredVestingDuration is also 0.)

  • Beneficiary is the vestingBeneficiary set at deployment (= original msg.sender of ignite).

  • Claim with FireToken.releaseVested(). Only the beneficiary can call - reverts with NotBeneficiary otherwise. Reverts with NothingToRelease if releasableAmount() == 0.

Putting it all together - what a creator earns

This is what a creator's earnings look like for a default frontend launch (creatorAllocationBps = 0):

Pre-graduation:

  • FuelVault creatorBuckets[creator][token] populated by buyer-initiated gifts. Each gift splits into liquid (withdrawable now) and vested (locked until graduation) per fuelVestedBps.

  • No trading-fee share (those go to the protocol on FireGraduator.claimFees).

  • No direct skim income. Skim credits the buyer, not the creator. Creators only see that value if buyers gift it.

At graduation:

  • FuelVault burn countdown starts (burnTime[token] = now + burnCountdownDuration). Affects only ungifted user credits, not creator buckets.

  • Creator's pre-grad vested bucket is eligible to unlock - actually unlocks on the creator's first post-grad withdraw.

  • FireToken vestingStart is set. With creatorAllocationBps = 0, this is a no-op.

Post-graduation:

  • Streaming share of trading fees via FireStream.claimFees() - creator receives 10000 - sum(registry.getStreamingRecipientsTotalBps()) of all fees, in both token0 and token1. This is the dominant income stream for a successful launch.

  • New gifts go fully to liquid (no more vesting split).

  • Creator can withdraw any time; first call after graduation also unlocks any prior vested bucket.

Custom launches with creatorAllocationBps > 0 add a third stream: linear FireToken vesting unlocked via releaseVested() over vestingDuration from graduation.

Errors to handle

Error
When

AlreadyGraduated

Calling FireGraduator.claimFees() after graduation (use FireStream.claimFees() instead)

NotGraduated

Calling FuelVault-graduation-gated functions too early

BurnNotReady / AlreadyBurned

executeBurn timing

InsufficientLiquid

Withdrawing more from FuelVault than the liquid bucket

NotBeneficiary

Wrong account collecting releaseVested() proceeds

NothingToRelease

releaseVested() called when nothing has vested

InvalidSignature / PermitExpired / InvalidNonce

EIP-712 issues on giftWithSig

Last updated