In this guide, you’ll read and claim the creator side of trading fees on Robinhood Chain. Each token has its own BagsFeeShare contract that accrues 1% of every trade (in WETH) and splits it among the claimers configured at launch.
Prerequisites
Before starting, make sure you have:
- Completed the Environment Setup.
- A token whose
feeShare address you can resolve (from launch, factory.feeShareForToken(token), or BagsLens.getTokenState).
- A wallet with a positive claimable balance — i.e. one of the token’s configured fee claimers or the optional partner. Check with
claimableOf(token, user) (or feeShare.claimable(user)).
1. How Fee Sharing Works
Of the flat 2% trade fee, half (1%) goes to the platform vault and half (1%) is delivered to the token’s BagsFeeShare, where it accrues in WETH. That full creator side is split among the recipients configured at launch — there is no separate protocol cut taken here. When fees are notified, the contract splits the notified amount:
partnerShare = amount * PARTNER_BPS / 10000 -> claimable[partner] (if a partner was set)
claimerPot = amount - partnerShare
each claimer : claimerPot * bps[i] / 10000 (last claimer absorbs rounding dust)
This mirrors the FeeNotified(amount, partnerShare, claimerPot) event: partnerShare + claimerPot == amount. Each recipient’s balance accumulates in claimable[address] until they call claim. This happens automatically as trades occur in both phases — creators earn from bonding-curve trades and pool trades alike.
Post-migration fees first accrue inside the Bags v4 hook and are periodically swept into the fee-share contract. claim pokes that sweep for you, so you don’t need a separate sweep transaction.
2. Read Claimable Fees
Use BagsLens.claimableOf(token, user) for the amount claimable right now (already notified to the fee-share, in WETH wei):
import { publicClient } from "./clients";
import { ROBINHOOD_LAUNCHPAD } from "./addresses";
import { bagsLensAbi } from "./abi";
import type { Address } from "viem";
export async function readClaimable(token: Address, user: Address): Promise<bigint> {
return publicClient.readContract({
address: ROBINHOOD_LAUNCHPAD.lens,
abi: bagsLensAbi,
functionName: "claimableOf",
args: [token, user],
});
}
You can also read directly from the fee-share contract:
| Call | Returns |
|---|
feeShare.claimable(user) | WETH wei claimable now for user |
feeShare.getClaimers() | (address[] claimers, uint16[] bps) |
feeShare.claimerBps(user) | A user’s share in bps |
feeShare.PARTNER() / feeShare.PARTNER_BPS() | Partner address and its bps |
claimableOf counts only WETH already notified to the fee-share. Fees still sitting un-swept in the v4 hook are not included; they become claimable once claim (or another sweep) flushes them.
3. Claim Fees
Call claim(unwrap) on the token’s BagsFeeShare. Pass unwrap: true to receive native ETH; false leaves the payout as WETH.
import "dotenv/config";
import { publicClient, getWalletClient } from "./clients";
import { bagsFeeShareAbi } from "./abi";
import { readClaimable } from "./read-claimable";
import type { Address } from "viem";
export async function claimFees(feeShare: Address, unwrap = true) {
const walletClient = getWalletClient();
const txHash = await (async () => {
// Simulate first so custom errors (e.g. BagsFeeShare_NothingToClaim) decode cleanly.
const { request } = await publicClient.simulateContract({
account: walletClient.account,
address: feeShare,
abi: bagsFeeShareAbi,
functionName: "claim",
args: [unwrap],
});
return walletClient.writeContract(request);
})();
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
if (receipt.status !== "success") throw new Error("Claim reverted.");
console.log(`Claimed. Tx: https://robinhoodchain.blockscout.com/tx/${txHash}`);
return { txHash };
}
// Example: resolve feeShare from the token, then claim as native ETH.
async function main() {
const token = "0xTOKEN_ADDRESS" as Address;
const user = getWalletClient().account.address;
const feeShare = await publicClient.readContract({
address: (await import("./addresses")).ROBINHOOD_LAUNCHPAD.factory,
abi: (await import("./abi")).bagsFactoryAbi,
functionName: "feeShareForToken",
args: [token],
}) as Address;
const claimable = await readClaimable(token, user);
if (claimable <= 0n) {
console.log("Nothing to claim yet.");
return;
}
console.log(`Claimable: ${claimable} WETH wei`);
await claimFees(feeShare, true);
}
main().catch(console.error);
Check claimableOf (or feeShare.claimable) is greater than zero before claiming. Calling claim with nothing to claim reverts with BagsFeeShare_NothingToClaim and wastes gas.
4. Estimate Pending (Un-Swept) Fees
For a fuller picture of what a creator will eventually receive, add the un-swept accrual sitting in the v4 hook for the token’s pool. The hook exposes per-pool pending fees; the creator side is half of it, split among the partner (if any) and the claimers by bps.
import { publicClient } from "./clients";
import { ROBINHOOD_LAUNCHPAD, ROBINHOOD_FEES } from "./addresses";
import { bagsLensAbi, bagsFeeShareAbi, bagsV4HookAbi } from "./abi";
import type { Address } from "viem";
/** Claimable-now plus an estimate of the user's cut of un-swept hook fees. */
export async function readClaimableBreakdown(token: Address, user: Address) {
const state = await publicClient.readContract({
address: ROBINHOOD_LAUNCHPAD.lens, abi: bagsLensAbi, functionName: "getTokenState", args: [token],
});
if (!state.exists) return null;
const [claimable, claimers, partnerBps, pool] = await Promise.all([
publicClient.readContract({ address: ROBINHOOD_LAUNCHPAD.lens, abi: bagsLensAbi, functionName: "claimableOf", args: [token, user] }),
publicClient.readContract({ address: state.feeShare, abi: bagsFeeShareAbi, functionName: "getClaimers" }),
publicClient.readContract({ address: state.feeShare, abi: bagsFeeShareAbi, functionName: "PARTNER_BPS" }),
publicClient.readContract({ address: ROBINHOOD_LAUNCHPAD.hook, abi: bagsV4HookAbi, functionName: "pools", args: [state.poolId] }),
]);
const [addresses, bps] = claimers;
const i = addresses.findIndex((a) => a.toLowerCase() === user.toLowerCase());
const userBps = i === -1 ? 0 : bps[i];
const pendingFees = pool[2]; // un-swept WETH accrual for this pool (both fee legs)
// On sweep the accrual splits 50/50: half to the platform vault, half is the
// creator side delivered to the fee-share (there is no separate protocol cut on
// that leg). A partner, if configured, takes PARTNER_BPS of the creator side
// before claimers split the remainder by bps. Multiply before dividing to limit
// truncation. This estimates a claimer's cut; a partner reads their own row.
const bpsDenom = BigInt(ROBINHOOD_FEES.bpsDenominator);
const creatorSide = pendingFees / 2n;
const partnerShare = (creatorSide * BigInt(partnerBps)) / bpsDenom;
const claimerPot = creatorSide - partnerShare;
const pendingUserEstimate = (claimerPot * BigInt(userBps)) / bpsDenom;
return { claimable, pendingFees, userBps, isClaimer: i !== -1, feeShare: state.feeShare, pendingUserEstimate };
}
The exact field layout of the hook’s pools(poolId) return tuple is defined in BagsV4Hook.json; pool[2] is the pending WETH accrual in the reference implementation. Confirm the index against the ABI you import. pendingUserEstimate is an estimate — the exact amount is settled when the sweep runs during a claim.
Troubleshooting
BagsFeeShare_NothingToClaim — your claimable balance is zero. Wait for trades to accrue fees (or for a sweep to flush hook fees), and check claimableOf before claiming.
BagsFeeShare_NotAuthorized — only the bonding curve and hook may notify fees; you can’t call notifyFee yourself. Just call claim.
- Claim succeeds but you received WETH, not ETH — you passed
unwrap: false. Pass true for native ETH, or unwrap the WETH yourself later.
- Not a claimer — only the token’s configured
claimers and its optional partner accrue fees. Verify eligibility with claimableOf(token, user) (or feeShare.claimable(user)) being greater than zero. Note feeShare.getClaimers() lists only the configured claimers — it does not include the partner.
For the full function, event, and error catalog, see the Contracts Reference.