Skip to main content
Claiming is one contract call, but a good claim button handles five things around it: the proof, the chain switch, a dry run, the receipt, and what to show afterwards. This component does all five in about sixty lines. This page assumes the client and helpers from Project Setup.

What It Does

  1. Fetches the proof for the wallet and campaign, treating a 404 as “nothing to claim” rather than an error.
  2. Switches the wallet to the reward chain if it is somewhere else.
  3. Simulates the claim before asking for a signature, so a revert becomes a readable message instead of a failed transaction.
  4. Submits and waits for the receipt.
  5. Marks the row claimed locally instead of re-fetching, because the API can trail the chain by a few minutes.

The Component

components/claim-button.tsx
Use it anywhere you have a campaign ID:

Where the Amount Comes From

The button reads claimable from the proof endpoint, not from the rewards list that renders the row around it. Only the proof is guaranteed to match what the contract will pay. A button driven by accumulatedRewards over-promises and reverts with InvalidProof(). See Show User Rewards.

Why Simulate

tbi.claim.simulate runs an eth_call and needs no signature, so it costs one RPC round trip and no wallet prompt. Every revert the contract can produce has a name, and revertReason carries it. The list is in Contracts.

After the Claim

Do not invalidate the proof query after a successful claim. The API is served from short caches and may still report the full amount as claimable while the contract has already paid it. A second click in that window reverts with NothingToClaim().
The onSuccess handler above writes the settled state into the cache directly. The next natural refresh, a page load or the rewards panel’s 60 second interval, picks up the real value once the API has caught up. If you need the authoritative answer immediately, read it from the chain; see Reading claim state on-chain.

Claim Everything at Once

A wallet with rewards in several campaigns on the same reward chain can settle all of them in one Multicall3 transaction.
components/claim-all-button.tsx
claimAll finds every campaign with claimable rewards on that chain, drops the empty ones, and submits one transaction. It is all or nothing: one failing subcall reverts the batch, and it throws TbiClaimError before touching the wallet if nothing is claimable. Render it only when at least one row is claimable, and fall back to per-row buttons if you want partial success. Details in Claim Several Campaigns at Once.

Sponsoring Gas or Skipping viem

The claim pays its user argument, not the sender, so your own relayer can submit it. Use tbi.encodeClaim to get { to, data, value } and send it from any signer, account-abstraction stack, or Safe. See Non-viem Stacks.

Keep Exploring

Claim Rewards

The full flow, batching, and gas expectations.

Errors

Which errors to catch and which retry themselves.

Deposit Button

For campaigns that require a Forwarder deposit.