Skip to content
bibo.DocsOpen app
Protocol documentation
DocsDevelopers

Fees and staking

Connect project fee balances, creator claims, the locked position's LP fees and rewards for staked Meme tokens.

7 min read
On this page

Resolve the project's fee contracts

Start with getLaunch(token). creatorFeeVault is the project's fee ledger; feeSplitter is the LaunchFeeSplitter that the launch's pool pays its additional fee to. A launch of LaunchFactory v1, from the first release, has no feeSplitter. From CreatorFeeVault, read quote(), policy(), currentRecipient() and stakingRewards(). A zero stakingRewards address means this project's policy does not allocate Holder Fee; do not show a staking action for it.

When a staking contract exists, read stakingToken() and rewardsToken(). The project's Meme token is staked and its quote asset is paid as rewards: BIBOCAT, WETH or BGB, whichever the launch pairs with. Staking creates an internal balance; it does not issue a receipt token, a staked Meme token or an NFT.

Resolve the project's fee contracts
Contract readWhat the balance means
CreatorFeeVault.creatorClaimable()Quote assets credited for the current creator recipient to claim.
CreatorFeeVault.holdersBudget()Quote assets waiting to be forwarded into the project's staking reward stream.
LaunchStakingRewards.balanceOf(account)Meme token principal currently staked by the account.
LaunchStakingRewards.earned(account)Quote-asset rewards currently accrued to that staker.
LaunchStakingRewards.totalSupply()Total Meme token principal staked in this reward contract.

Bring project fees into the vault

The curve accrues the project's allocated launch fees and pays them straight to CreatorFeeVault. Its protocol share, the part the App labels Platform, goes to ProtocolFeeEscrow. After graduation the launch's pool charges one additional fee on the quote-asset side, creatorFeePips(): the Creator Fee plus the platform fee. The pool pays it to its creatorFeeVault(), which is the launch's LaunchFeeSplitter, not the vault. The splitter sends the platform part to ProtocolFeeEscrow and credits the rest to CreatorFeeVault, where it follows the project's fixed creator–holder allocation. The pool's market fee is LP income with separate accounting; the graduation position's share is collected through LaunchLiquidityLocker, below.

Fees waiting at the source are not yet part of creatorClaimable() or holdersBudget(). Collection moves them to their configured destinations; triggering it does not pay the caller.

// BondingCurve
function flushFees() external;

// The launch's CLPool
function creatorFeePips() external view returns (uint24);
function creatorFeeVault() external view returns (address);
function collectCreatorFees() external returns (uint256 amount);

// LaunchFeeSplitter
function creatorFeePips() external view returns (uint24);
function platformFeePips() external view returns (uint24);
function totalFeePips() external view returns (uint24);

event FeesSplit(uint256 received, uint256 creatorAmount, uint256 platformAmount);
Curve fees
Read pendingCreatorFee() on BondingCurve. The permissionless flushFees() call forwards project fees to CreatorFeeVault and protocol fees to their escrow. Graduation also flushes these balances as part of the sweep, so a successfully swept curve has no remaining accrued fees.
Pool fees
Read creatorFees() on the pool: it accrues the Creator Fee and the platform fee together. Simulate the permissionless collectCreatorFees() to get the amount the pool will send to the splitter; it can be smaller than creatorFees(). That amount is not the project's income. The splitter sends mulDiv(amount, platformFeePips, totalFeePips) to ProtocolFeeEscrow and the remainder to CreatorFeeVault; with no Creator Fee, all of it is platform fee. A pool whose creatorFeeVault() is zero rejects the call.
Confirm the credit
In the same transaction, LaunchFeeSplitter emits FeesSplit(received, creatorAmount, platformAmount) and, when creatorAmount is nonzero, CreatorFeeVault emits CreatorFeeCredited(source, amount, creatorLeg, holdersLeg) with the splitter as source. Curve credits name the curve as source. Refresh vault balances after confirmation. Directly transferring tokens to the vault does not create a fee claim.

Collect the graduation position's LP fees

Graduation mints a full-range position in the launch's pool and locks it in LaunchLiquidityLocker for good; no call withdraws its liquidity. Its share of the pool's market fee is paid to one LP fee recipient per launch. At graduation that recipient is the protocol fee recipient, so these LP fees go to the protocol unless it hands them over. A v1 launch's locker has no collectFees and no recipient: its position's LP fees are never collected.

Read the locker from the launch's recorded GraduationManager with locker(), then read lockedPositionOf(token), feeRecipientOf(token) and pendingFeeRecipientOf(token). collectFees(token) is permissionless: it collects the position's owed fees and always pays feeRecipientOf(token), so the caller cannot redirect them. It returns amount0 and amount1 in the pool's token order; simulate it to preview the payout.

// LaunchLiquidityLocker
function lockedPositionOf(address token) external view returns (uint256);
function feeRecipientOf(address token) external view returns (address);
function pendingFeeRecipientOf(address token) external view returns (address);

function collectFees(address token)
    external returns (uint256 amount0, uint256 amount1);

function proposeFeeRecipient(address token, address newRecipient) external;
function cancelFeeRecipientHandoff(address token) external;
function acceptFeeRecipient(address token) external;
Hand the LP fees to a new recipient
The current recipient calls proposeFeeRecipient(token, newRecipient), and the proposed address calls acceptFeeRecipient(token). Acceptance moves uncollected and future LP fees, because every collection pays the recipient of that moment. Until then, the current recipient can call cancelFeeRecipientHandoff(token).
Handoff errors
Only the current recipient can propose or cancel (NotFeeRecipient), and only the pending address can accept (NotPendingFeeRecipient). Cancelling with no proposal reverts with NoPendingHandoff. Proposing the zero address or the current recipient reverts with ZeroAddress.
Events
FeesCollected(token, positionId, recipient, amount0, amount1) records each payout. FeeRecipientHandoffProposed, FeeRecipientHandoffAccepted and FeeRecipientHandoffCancelled record the handoff.
Separate from creator income
The LP fee recipient and CreatorFeeVault's creator recipient are separate rights with separate handoffs. Changing one leaves the other unchanged.

Claim creator income

Read currentRecipient() and creatorClaimable(), then simulate claimCreator() from the current recipient. It pays the full claimable creator balance in the quote asset. The launch's original creator address is attribution; it is not a substitute for checking the current recipient.

// CreatorFeeVault
function claimCreator() external returns (uint256 amount);
Recipient changes
If your integration supports changing the payout wallet, the current recipient calls proposeRecipient(newRecipient), then the proposed wallet calls acceptRecipient(). The handoff moves both already-accrued unclaimed income and future creator income. cancelRecipientHandoff() lets the current recipient cancel a pending proposal.
Confirmation
CreatorLegClaimed(recipient, amount) records the payout. A wrong caller reverts with NotRecipient; an empty claim reverts with ZeroAmount.

Forward the Holder Fee budget

forwardHoldersLeg() moves the vault's holdersBudget into LaunchStakingRewards, where rewards stream to Meme token stakers. This is a permissionless funding action, separate from an individual staker's claim.

// CreatorFeeVault
function forwardHoldersLeg() external returns (uint256 amount);
Check the forwarding window
Read holdersBudget(), stakingRewards(), lastForwardAt() and EPOCH_DURATION(). Forwarding can succeed at most once per aligned protocol epoch. Use the current block timestamp and simulate the call to confirm availability.
Handle a deferred stream
EpochAlreadyForwarded means the current epoch has already been funded. NothingStaked or ZeroRewardRate means the staking contract cannot start this stream yet. A reverted forward leaves the budget in the vault.
Refresh after funding
HoldersLegForwarded records the forwarded amount. Read rewardRate(), periodFinish() and earned(account) from LaunchStakingRewards. The vault's budget is not an amount every holder can immediately claim.

Stake Meme tokens and claim rewards

Approve the project's Meme token to its LaunchStakingRewards contract, then call stake(amount) from the participating wallet. Wallet holdings alone earn no Holder Fee. All four actions below apply to the caller's own stake and rewards.

// LaunchStakingRewards
function stake(uint256 amount) external;
function withdraw(uint256 amount) external;
function getReward() external;
function exit() external;

function balanceOf(address account) external view returns (uint256);
function earned(address account) external view returns (uint256);
Stake Meme tokens and claim rewards
ActionEffect
stake(amount)Adds Meme principal and restarts the withdrawal delay for the account's entire stake.
withdraw(amount)Returns the requested Meme principal once the delay has elapsed. It does not claim rewards.
getReward()Pays accrued quote-asset rewards. Does not change the withdrawal time and can be called while principal is locked.
exit()Withdraws all principal and claims rewards. Requires a nonzero stake and an elapsed withdrawal delay. If only rewards remain, use getReward().

Display withdrawal timing and confirmed activity

Read lastStakedAt(account) and UNBOND_DELAY() to calculate when principal becomes withdrawable. The deployed contracts use a six-hour delay; reading the getter keeps the integration tied to its deployment. Compare the result with the chain's block timestamp. A top-up moves the withdrawal time for the entire balance, while a reward claim does not.

Use Staked(user, amount), Withdrawn(user, amount) and RewardPaid(user, reward) to update activity, then refresh balanceOf(account) and earned(account). UnbondDelayNotElapsed(availableAt) provides the earliest withdrawal timestamp when a withdrawal is attempted too soon.

BIBO ProtocolLaunchpad + ve(3,3) liquidity.