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

Rewards

The BaseRewardPool.sol contract is used as the main reward contract for all LP pools. It is a synthetix-inspired staking contract: users stake their Convex deposit token and accrue CRV (and other reward tokens) at the pool's rewardRate until periodFinish.

//sample convex reward contracts interface
interface IConvexRewards{
    //get balance of an address
    function balanceOf(address _account) external view returns(uint256);
    //withdraw to a convex tokenized deposit
    function withdraw(uint256 _amount, bool _claim) external returns(bool);
    //withdraw directly to curve LP token
    function withdrawAndUnwrap(uint256 _amount, bool _claim) external returns(bool);
    //claim rewards
    function getReward() external returns(bool);
    //stake a convex tokenized deposit
    function stake(uint256 _amount) external returns(bool);
    //stake a convex tokenized deposit for another address(transfering ownership)
    function stakeFor(address _account,uint256 _amount) external returns(bool);
}

Token Type of Reward

Use baseRewardPool.rewardToken() to determine what token is rewarded

Balance of an Account

Use baseRewardPool.balanceOf(address) to see how much an address has staked.

Total Staked Amount

Use baseRewardPool.totalSupply() to see how much is currently staked on the reward contract

Current Earned Rewards

Use baseRewardPool.earned(address) to see how much rewards an address will receive if they claim their rewards now.

Claim Rewards

Use baseRewardPool.getReward() or baseRewardPool.getReward( address, bool ) to claim rewards for your address or an arbitrary address. The bool is an option to also claim extra incentive tokens (ex. SNX, CVX) which is defaulted to true in the non-parametrized version. More information on extra rewards below.

Stake/Deposit Tokens

Use baseRewardPool.stake( amount ) to stake a given amount for your address: Use baseRewardPool.stakeAll() to stake all tokens for your address Use baseRewardPool.stakeFor( address, amount ) to stake on behalf of another address (*This transfers ownership of the tokens!)

Unstake/Withdraw Tokens

Use baseRewardPool.withdraw( amount, bool ) to withdraw a specific amount. bool is to also claim while withdrawing Use baseRewardPool.withdrawAll( bool ) to withdraw all tokens staked. bool is to also claim while withdrawing\ Use baseRewardPool.withdrawAndUnwrap( amount, bool ) to withdraw a specific amount of tokens AND also unwrap the tokens back into the underlying LP tokens. bool is to also claim while withdrawing Use baseRewardPool.withdrawAllAndUnwrap(bool claim) to withdraw all tokens AND also unwrap the tokens back into the underlying LP tokens. bool is to also claim while withdrawing

Extra Rewards

The BaseRewardPool has an array of child reward contracts called extraRewards. You can query the number of extra rewards via baseRewardPool.extraRewardsLength(). This array holds a list of VirtualBalanceRewardPool contracts which are similar in nature to the base reward contract but without actual control of staked tokens. This means that if a pool has CRV rewards as well as SNX rewards, the pool's main reward contract(BaseRewardPool) will distribute the CRV and the child contract(VirtualBalanceRewardPool) will distribute the SNX.

Important for pools with IDs 151+:

VirtualBalanceRewardPool's rewardToken points to a wrapped version of the underlying token. This Token implementation can be found here: https://github.com/convex-eth/platform/blob/main/contracts/contracts/StashTokenWrapper.sol

A staking implementation example handling these wrappers can be found at: https://github.com/convex-eth/platform/blob/main/contracts/contracts/wrappers/ConvexStakingWrapper.sol

Reward hooks & the earmark flow

The mainnet BaseRewardPool itself does not call a reward hook. Hooks are wired on the stash side of the earmark flow: when the Booster's earmarkRewards(_pid) harvests a pool, it calls the pool's stash (e.g. ExtraRewardStashV3), which claims gauge rewards, and for supported stash versions — invokes a configured RewardHook.sol via onRewardClaim(). That hook may notify extra rewards or external incentive contracts so that incentive tokens get queued into the pool's child reward contracts.

Practical consequences for integrators:

  • Extra rewards on a pool accrue through the child VirtualBalanceRewardPool contracts (extraRewards), not through hooks on BaseRewardPool.

  • CVX minted for a claimant is handled by the claim itself: BaseRewardPool.getReward() calls back to Booster.rewardClaimed(), which triggers CvxMining.ConvertCrvToCvx and mints CVX to the claimant (see CVX Minting). It is not delivered as a separate extra reward and there is no "Chef hook" mint on the main platform.

  • earned()/getReward() for CRV are accurate on-demand; extra-reward accrual is made current by the earmark process, so a fresh earmark before reading/claiming is good practice for very recent gauge harvests but not strictly required.

Other Information

The reward contract mechanics are that of popular synthetix style reward contract. Some important parameters for determining things like APY could be: duration: The reward contract cycle length periodFinish: When the current cycle is planned to finish rewardRate: How many tokens are distributed per second

Donating / adding rewards

  • donate(_amount) is permissionless — anyone can donate the reward token; it transfers tokens into the contract and adds to queuedRewards. Whether queued rewards are notified into the active stream (vs held queued) is decided by queueNewRewards() against the contract's newRewardRatio constant (830).

  • queueNewRewards(_rewards) is operator-only (the Booster) — it combines the new amount with any queuedRewards, then either notifies the reward stream immediately or keeps the combined amount queued, based on the newRewardRatio check.

  • There is no external notifyRewardAmount on BaseRewardPool. The notifyRewardAmount-surface contracts (e.g. the staking/reward contracts used by the cvxFXS/cvxFXN platforms) are separate contracts with their own ABIs. Always check the ABI of the specific deployed reward contract you integrate against rather than assuming a shared surface.

Last updated

Was this helpful?