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) / 10000So 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
claimFeescall. 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 of10000 - totalBpsof all future fees on every graduated token.Max 10 protocol recipients, each with a
bpsshare. Sum of bps cannot exceed 10000. Anything left over goes to the creator.Creator address comes from
FireLaunch.getLaunchState(token).creator- set atignitetime, never changed by the contracts. (FireStream also pins the creator on its owntokenInfo[token].creatorfield at graduation whenreceiveNFT(token, nftId, creator)is called, so post-grad reads can also come from FireStream.) The "Claim" UI onkumbaya.xyzis 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.
FuelVaultis exposed onkumbaya.xyzas the Tip Jar. Thegift/giftWithSigcalls 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
liquidandvestedportions perregistry.fuelVestedBps()if post-graduation: 100% goes to
liquid(no further locking)reverts with
SelfGiftNotAllowedifuser == 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 adeposit(beneficiary, token, amount)entry point that adds to a beneficiary'screditsbalance, used for protocol-level seeding.
Two distinct vesting concepts (don't conflate them)
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 vested → liquid 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 callignite()yourself with a non-zerocreatorAllocationBps.
The schedule:
Starts at graduation (not at launch). Pre-graduation,
vestedAmount() == 0(becausevestingStart == 0).No cliff. Linear from
vestingStartovervestingDurationseconds. The frontend default is 90 days; the registry-allowed range is[90 days, 730 days].vestingDuration == 0is a special case: everything is fully vested immediately at graduation. (Only reachable for launches withcreatorAllocationBps > 0andcreatorGiftBalanceCaptureBps < 10000if registry'srequiredVestingDurationis also 0.)Beneficiary is the
vestingBeneficiaryset at deployment (= originalmsg.senderofignite).Claim with
FireToken.releaseVested(). Only the beneficiary can call - reverts withNotBeneficiaryotherwise. Reverts withNothingToReleaseifreleasableAmount() == 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 intoliquid(withdrawable now) andvested(locked until graduation) perfuelVestedBps.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
vestingStartis set. WithcreatorAllocationBps = 0, this is a no-op.
Post-graduation:
Streaming share of trading fees via
FireStream.claimFees()- creator receives10000 - 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
withdrawany 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
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