Referrals need SDK
0.3.0 or later. Both the referee and the referrer must have wallets linked to a Rabbithole account; binds between unlinked wallets are rejected.The Flow
- Capture the referral code from the link: a wallet address or a Rabbithole username.
- Bind the referee with a signed message, before they sign their first deposit.
- Show the referrer their per-campaign earnings.
- Claim each payout once its campaign has finalized, before the claim window closes.
Capture the Code
A referral link carries the referrer’s address or their Rabbithole username. Neither is a secret, and there is nothing to register. Read it however your routing works, and keep it until the user deposits:Accept a Username Link
The SDK’s bind takes an address, because the referee signs over the address. When the link carries a username, resolve it to the account’s payout wallet with the transport escape hatch first:Bind Before the Deposit
The referee’s wallet signs an EIP-712 message and the SDK submits it. The wallet can be on any network: the message has nochainId, because a bind applies to every campaign on every chain.
Handle Rejections
Rejections areTbiApiErrors with a machine-readable code. None of them should block the deposit itself; the user can still deposit, they just will not be attributed.
walletClient with no account, and a deadline that is already past or more than an hour away. The default deadline is ten minutes from now, so you only hit the second one if you pass your own.
Signing Outside viem
Build the exact payload withreferralBindTypedData, sign it with the referee’s EOA through your own stack, and submit the signed fields with submitBind:
{ name: "Boost TBI Referrals", version: "1" } with no chainId, and the message type is ReferralBind { address referrer; uint256 deadline }. Sign referrer exactly as you submit it. Over raw REST, deadline travels as a decimal string.
Show Earnings
referrals.stats returns one entry per campaign where the referrer has counted referees:
{ chainId } as the second argument to limit the list to one reward chain.
How to present the two states:
"estimated": the campaign is live. Label the number as an estimate. It moves with your referees’ earnings and with every other referrer’s, because all of them share one pool."final": the payout is settled. OnceclaimWindowEndis set, the payout is claimable until then. Show the deadline next to the button.
claimTxHash and claimedAt mark a payout as claimed, but they trail the chain by the indexer’s confirmation delay. Use them for history, not to gate the button.
Claim the Payout
Referral payouts have their own claim flow.tbi.claim and tbi.claimAll do not apply.
Fetch the Proof
Gate, Simulate, Submit
TbiClaimError before submitting when the wallet is on the wrong chain, the proof does not match the campaign or referrer, the root is not published yet, or the payout is zero. simulate reports those last two as willSucceed: false instead of throwing.
Claiming is permissionless: any wallet can submit, and the tokens always go to the referrer in the proof. That makes relayer-sponsored claims work out of the box.
Non-viem Stacks
to is the TBI Manager, not the distributor. The distributor only accepts calls from the Manager.
After the Claim
As with reward claims, do not re-read the API to decide whether the claim went through. Mark the payout claimed once you have a receipt, or readclaimed(referrer) on proof.distributorAddress for the authoritative answer. See Referral Claims.
Keep Exploring
How Referrals Work
Binding rules, the pool cap, and the claim window.
Errors
Every bind rejection code and what it means.
Contracts
claimReferral and the distributor’s revert selectors.