From Accrual to Roots
Rewards are computed off-chain by Boost’s indexers and committed as merkle roots on the reward chain. Users claim against the TBI Manager with a proof the API hands out. Roots publish periodically while the campaign runs, not only at the end, so claims open during a campaign rather than after it. A proof withpublishedAt: null means a tree exists but its root is not on-chain yet; nothing is claimable until it is.
Cumulative-Distributor Semantics
The merkle leaf holds the user’s lifetime reward total, not the delta since their last claim.claimable exists for display. Re-submitting a fully-claimed proof is a no-op or a revert, never a double payment.
The Three Numbers
A user’s rewards surface as three different values, and they are supposed to differ.
Show
accumulatedRewards in the earned row, since it is the number that moves and the one users want to watch. Show claims.get().claimable on the button.
Two Clocks
Accrual and claimability move on different clocks, and users notice.- Accrual updates continuously. The earned figure climbs every time you refresh.
- Claimability steps only when a new merkle root is published on-chain.
Roots publish periodically, on the order of hours rather than seconds. The exact cadence is not a published commitment, so point users at the next distribution rather than a countdown.
Who Can Submit
The claim function pays theuser argument, independent of msg.sender. Anyone can submit a claim on a user’s behalf and the rewards still land with the user.
That means relayer and account-abstraction flows work with no special support, and you can sponsor gas for your users without ever holding their rewards. Contracts covers the calldata and the measured gas cost.
Batch Claims
claimAll batches several claims into one Multicall3 aggregate transaction. Two constraints:
- Every campaign in the batch must share one reward chain.
- The batch is all-or-nothing: if one subcall reverts, the whole transaction reverts.
tbi.claim() per campaign instead.
Linked Claim Addresses
For campaigns where positions are tracked on a different chain than rewards are claimed (prediction-market campaigns, typically), the claiming address on the reward chain is linked to the trading address on the event chain, and permanently locked per campaign to prevent double claims. Query claims with the reward-chain address the user linked.Cliff State on Proofs
Campaigns runningcliff mode require users to hold their position for cliffDurationSeconds from their trigger. Dropping below their peak balance by more than buffer breaks the cliff and forfeits everything accrued.
A claim proof for a cliff campaign carries that state:
cliffPositions[].statusis"intact"(still in the hold window),"passed"(completed), or"broken"(forfeited), withendsAtmarking completion.cliffBreakdescribes the balance drop that broke it, when one occurred.
Cliff fields are part of the API surface and are
null on campaigns that do not use the mode. Confirm availability with the Boost team before designing a campaign around it.Claim Windows
Claims do not stay open forever; theClaimExpired() revert exists for exactly this. Standard campaigns allow 60 days after the campaign ends, and the window is configurable per campaign. Once it closes, unclaimed rewards become recoverable by the campaign creator.
The claim deadline is not currently exposed as an API field. If your UI needs to show a countdown, ask the Boost team for the window on your specific campaign. See Campaign Lifecycle for the phase this belongs to.
Keep Exploring
Claim Rewards
The implementation: fetch, gate, simulate, submit.
Show User Rewards
Rendering earned versus claimable, and the states in between.
Contracts
The Manager, the claim signature, and revert selectors.