--- 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. --- # LienFi Airdrop — 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. ## Constants | 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. ## Step-by-step ### 1. Generate the merkle proof for the connected wallet 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 "] ) ``` 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 `1` with `NOT_ELIGIBLE: ...` 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 as `PROOF` for step 2. ### 2. Submit the claim 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=[''], value="0", chain="base" ) ``` Notes: - `args` is a **JS array of strings**. The single string element is itself a **JSON-encoded array** of `bytes32` proof hashes — exactly what step 1 printed, no transformation needed. - `value` is `"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. ### 3. Post-claim verification After `write_contract` returns a successful `transactionHash`: 1. Read `hasClaimed()` on the airdrop contract — should now be `true`. 2. Read `balanceOf()` on the LFI token (`0x3722264aB15a1dfCe5a5af89e6547F7949A8ABA3`) — should be at least `2000000000000000000000000` higher than before. Report the tx hash and the new LFI balance to the user. ## Common revert reasons | 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. | ## Owner-only paths (do NOT use unless the connected wallet is the owner) 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 with `CannotRescueAirdropToken()` if called with the LFI address — use `sweep` for that. - `setToken(address token)` — already called at deploy time. A second call reverts with `TokenAlreadySet()`. There is nothing more to do here. Never call these on behalf of a non-owner. They will revert with `OwnableUnauthorizedAccount()`.