Scan, claim, and swap legacy (v0–v3.1) Clanker creator fees for a given creator address on Base — using Bankr to sign and submit transactions by default, so you never need to export a raw private key (though a local-signer mode is available too — see below).
Legacy Clanker tokens can have unclaimed value under either or both of these — they are unrelated, so this tool checks both:
-
Safe/Merkle mechanism — a contract called
ClankerSafeErc20Spender(0x10F4485d6f90239B72c6A5eaD2F2320993D285E4) holds accumulated fees in the token itself (not WETH) for each legacy token, gated by a Merkle-proof-based creator registry built from Clanker's published creator snapshot (bundled with theclanker-sdknpm package). Claiming requires:initializeTokenCreator(token, newCreator, proof)— a one-time registration per token.tokenCreatorTransfer(safe, token, recipient)— moves the balance from whichever Safe wallet holds it.
There's no on-chain registry mapping each token to its Safe, so this repo discovers the correct Safe empirically (see
KNOWN_SAFESinscan-legacy-rewards.mjs) by checking which one actually holds a nonzero balance. -
Standard LP-fee mechanism — ordinary Uniswap V3 LP trading fees, claimed directly via
factory.claimRewards(token)on the v2/v3/v3.1 factory contracts. No init step, but also no way to know the payout in advance — a successful call can still yield exactly zero if nothing has accrued. This mechanism is easy to miss if you only check the Safe balance; a token can show zero there and still have real value here, or vice versa.
| Script | Purpose |
|---|---|
scan-legacy-rewards.mjs |
Read-only. Finds every legacy token recorded for CREATOR_ADDRESS, checks both mechanisms' live on-chain state, computes Merkle proofs, and reports what's actually claimable. No wallet or API key needed. |
claim-legacy-rewards.mjs |
Submits the real claim transactions for both mechanisms found by the scan. Idempotent — safe to re-run, it skips anything already done. Supports Bankr or a local private key. |
swap-to-eth.mjs |
Swaps claimed token balances to ETH via Bankr's Agent API, which routes through Uniswap and — importantly — refuses tokens with no real liquidity rather than executing a near-worthless swap. Bankr-only (no local-signer equivalent). |
force-swap.mjs |
For tokens you've already confirmed are illiquid but still want swapped anyway. Bypasses Bankr's safety check and swaps directly via Uniswap V3 SwapRouter02 with zero slippage protection. Supports Bankr or a local private key. Only use this on tokens you've already verified are effectively worthless — see warning in the file. |
npm installOption A — Bankr (recommended, no raw private key):
- Wallet API access, read-write (not the read-only default) — required for
claimandforce-swap. - Agent API access, plus either Bankr Club or LLM credits — required for
swap-to-eth.mjsspecifically (403 subscription_requiredotherwise; fall back toforce-swap.mjsif you don't have this).
Option B — local private key: set PRIVATE_KEY instead of BANKR_API_KEY for claim-legacy-rewards.mjs / force-swap.mjs. There's no local-signer path for swap-to-eth.mjs (it inherently needs Bankr's agent for liquidity-aware routing) — use force-swap.mjs instead if you're not using Bankr at all.
Never commit a secret or paste it anywhere public. Store it in a local .env file (already gitignored) or an environment variable:
BANKR_API_KEY=bk_your_key_here
# or, for local signing:
PRIVATE_KEY=0xyour_private_key_here
# 1. Scan (read-only, no key needed) — checks BOTH mechanisms automatically
CREATOR_ADDRESS=0xYourCreatorAddress node scan-legacy-rewards.mjs > final-report.json
# 2. Review final-report.json, then claim everything found
RECIPIENT_ADDRESS=0xYourRecipient BANKR_API_KEY=bk_... node claim-legacy-rewards.mjs
# ...or with a local key:
RECIPIENT_ADDRESS=0xYourRecipient PRIVATE_KEY=0x... node claim-legacy-rewards.mjs
# 3. Swap claimed tokens to ETH (needs Agent API + Bankr Club/credits)
RECIPIENT_ADDRESS=0xYourRecipient BANKR_API_KEY=bk_... node swap-to-eth.mjs
# 4. If step 3 hits the subscription gate, or for known-illiquid leftovers: force-swap directly
RECIPIENT_ADDRESS=0xYourRecipient BANKR_API_KEY=bk_... node force-swap.mjsCREATOR_ADDRESS and RECIPIENT_ADDRESS are usually the same wallet, but don't have to be for the Safe mechanism — claim-legacy-rewards.mjs sets RECIPIENT_ADDRESS as both the destination for claimed tokens and the new on-chain creator record. The actual transaction signer must equal RECIPIENT_ADDRESS exactly (whether that's your Bankr wallet or a PRIVATE_KEY's derived address) — the Merkle proof validates against msg.sender, and the script hard-fails if a provided PRIVATE_KEY doesn't match.
| Variable | Required for | Description |
|---|---|---|
CREATOR_ADDRESS |
scan | The 0x address to look up legacy tokens for |
RECIPIENT_ADDRESS |
claim, swap, force-swap | Must match the address that actually signs |
BANKR_API_KEY |
claim, force-swap; required for swap | Read-write Wallet API access; Agent API + Bankr Club/credits needed for swap-to-eth.mjs specifically |
PRIVATE_KEY |
claim, force-swap (alternative to BANKR_API_KEY) |
Local signer. Takes priority over BANKR_API_KEY if both are set. No equivalent for swap-to-eth.mjs. |
BANKR_API_URL |
all (Bankr) | Defaults to https://api.bankr.bot |
RPC_URL |
all | Defaults to https://mainnet.base.org; the public endpoint rate-limits under heavy load, all scripts retry automatically but a private RPC will be faster and more reliable |
- Real money moves here. Every step past the scan submits actual on-chain transactions. Read the scripts before running them.
- The
msg.senderon theinitializeTokenCreatorcall must exactly match the address recorded as the token's creator in Clanker's snapshot — the Merkle proof will not validate for any other address, even if you pass a differentnewCreator. - Many legacy tokens are years-old, abandoned meme coins with zero real liquidity. The scan will still report them as claimable under either mechanism if a nonzero balance exists — that doesn't mean the tokens are worth anything.
force-swap.mjssetsamountOutMinimumto0, meaning no slippage or MEV protection. Only use it on tokens you've already confirmed have negligible real liquidity (e.g. by checking a pool's actual paired-asset reserves, not just itsliquidity()value, which can look nonzero while holding almost nothing of value).- Don't trust an immediate before/after balance diff as proof of "nothing received." Base's public RPC is load-balanced across nodes; a read moments after a transaction can land on a node that hasn't caught up yet and silently return a stale value. This produced real false "zero" reports in practice, on claims that actually delivered large amounts.
claim-legacy-rewards.mjshandles this correctly for the LP-fee mechanism: it submits all claims first, waits, then does a separate, heavily-retried absolute-balance pass (verifiedBalanceFormattedin the output) — trust that field, not a same-call diff. - Duplicate token symbols across different contract addresses are common among legacy tokens. Always operate by contract address, never by symbol/name.
MIT