| name | lienfi-airdrop-claim |
|---|---|
| description | Claim from the LienFi (LFI) airdrop on Base mainnet. 500 pre-approved Bankr Club holders can each claim 2,000,000 LFI once via merkle proof. Use when the user asks to "claim my LFI airdrop", "claim LienFi", "check if my wallet is on the LFI list", or any wallet on the eligible list asks about an LFI claim. |
A live equal-share merkle airdrop deployed to Base mainnet (chain id 8453, chain name base). 500 pre-approved addresses can each call claim(bytes32[]) once and receive 2,000,000 LFI. Eligibility is enforced on-chain by a merkle proof; the leaf hashes msg.sender, so each recipient must claim from their own wallet — there is no third-party / sponsored-claim path.
| Field | Value |
|---|---|
| Chain | base (id 8453) |
| BankrAirdrop | 0x869c8d05F364E42AaDAaAC0e72Afb4CA4E1A1530 |
| LFI token (18 dec) | 0x3722264aB15a1dfCe5a5af89e6547F7949A8ABA3 |
| Merkle root | 0x1dca93fc5f93e2ee469afbb8c1459b55bfb6b7fe7def57de84c341462349079b |
| Share per address | 2000000000000000000000000 wei (= 2,000,000 LFI) |
| Total claimers | 500 |
| Claim deadline | unix 1780245296 — Sun 31 May 2026 ~09:35 PT |
| Owner (can sweep at any time) | 0xa96A29a713AD3e59249180831A0b902385C2dEa2 |
| Proofs URL | https://gist.githubusercontent.com/0xdeployer/c7b78c9b3d311685f8987127808b3615/raw/06dce68c4141fb1cc89fc2bb8e8eb6ae457c55e6/proofs.json |
The proofs file is keyed by checksummed address. The merkle root inside that file MUST match the Merkle root constant above — re-check both before trusting any proof you pull from it.
Use execute_cli to run a small inline script that fetches the hosted proofs file and prints the proof for the connected wallet (case-insensitive lookup). If the wallet isn't on the list the script exits non-zero with a clear message — that is the eligibility check, do NOT then call write_contract.
execute_cli(
files='{"getProof.ts": "const url = \"https://gist.githubusercontent.com/0xdeployer/c7b78c9b3d311685f8987127808b3615/raw/06dce68c4141fb1cc89fc2bb8e8eb6ae457c55e6/proofs.json\";\nconst target = (process.argv[2] ?? \"\").trim().toLowerCase();\nif (!/^0x[a-f0-9]{40}$/.test(target)) { console.error(\"usage: bun getProof.ts <0x...wallet>\"); process.exit(2); }\nconst data = await (await fetch(url)).json();\nconst expectedRoot = \"0x1dca93fc5f93e2ee469afbb8c1459b55bfb6b7fe7def57de84c341462349079b\";\nif (data.merkleRoot.toLowerCase() !== expectedRoot.toLowerCase()) { console.error(\"merkle root mismatch — refusing to proceed\"); process.exit(3); }\nconst entry = Object.entries(data.proofs).find(([k]) => k.toLowerCase() === target);\nif (!entry) { console.error(\"NOT_ELIGIBLE: \" + target + \" is not in the LFI airdrop list\"); process.exit(1); }\nconsole.log(JSON.stringify(entry[1]));\n"}',
commands=["bun getProof.ts <CONNECTED_WALLET>"]
)
The script:
- Fetches the proofs file from the URL above.
- Verifies the file's merkle root matches the constant — refuses to continue if it doesn't.
- Returns exit code
1withNOT_ELIGIBLE: <addr> ...if the wallet isn't on the list — translate this for the user verbatim and stop. - On success, prints a single JSON line: a
bytes32[]proof (8 or 9 hashes). Capture it asPROOFfor step 2.
Use write_contract. The proof is a single bytes32[] argument; the tool expects it as a JSON-array string. Pass through exactly what the script in step 1 printed.
write_contract(
to="0x869c8d05F364E42AaDAaAC0e72Afb4CA4E1A1530",
functionSignature="claim(bytes32[] proof)",
args=['<PROOF from step 1, e.g. ["0x75cd…","0xe7e8…",…]>'],
value="0",
chain="base"
)
Notes:
argsis a JS array of strings. The single string element is itself a JSON-encoded array ofbytes32proof hashes — exactly what step 1 printed, no transformation needed.valueis"0". The claim is not payable.- The function does NOT take the recipient as an argument — eligibility is determined by
msg.sender.
If the call reverts, decode the revert selector against the table below and surface a plain-English message to the user. Do NOT retry blindly.
After write_contract returns a successful transactionHash:
- Read
hasClaimed(<wallet>)on the airdrop contract — should now betrue. - Read
balanceOf(<wallet>)on the LFI token (0x3722264aB15a1dfCe5a5af89e6547F7949A8ABA3) — should be at least2000000000000000000000000higher than before.
Report the tx hash and the new LFI balance to the user.
| Custom error | Selector | Meaning | What to tell the user |
|---|---|---|---|
InvalidProof() |
0x09bde339 |
Wallet not in the merkle tree, or wrong proof passed | The connected wallet is not on the LFI airdrop list. |
AlreadyClaimed() |
0x646cf558 |
This wallet already claimed | Already claimed. Show the prior tx if you can find it. |
ClaimWindowClosed() |
0xf0f25a33 |
block.timestamp > claimDeadline |
The claim window closed on Sun 31 May 2026 ~09:35 PT. |
InsufficientBalance() |
0xf4d678b8 |
Contract balance < share (owner swept) | The owner swept the airdrop; claims are no longer possible. |
The owner is 0xa96A29a713AD3e59249180831A0b902385C2dEa2. If — and only if — the connected wallet matches:
sweep(address to)— pulls all remaining LFI out of the airdrop. Emergency escape hatch + post-deadline cleanup.rescueERC20(address token, address to)— recovers any non-LFI ERC20 accidentally sent to the contract. Reverts withCannotRescueAirdropToken()if called with the LFI address — usesweepfor that.setToken(address token)— already called at deploy time. A second call reverts withTokenAlreadySet(). There is nothing more to do here.
Never call these on behalf of a non-owner. They will revert with OwnableUnauthorizedAccount(<caller>).