Issue #4 — Merkle-Tree Airdrop Contract Extension
Labels: contract feature soroban airdrop
Summary
Add a Merkle-tree-based airdrop mechanism to FinchippayContract enabling gas-efficient token distributions to thousands of recipients.
Background
The current batch_send function (lib.rs line ~590) is limited to 50 recipients per call and requires the sender to pay gas for every transfer inline. For large-scale airdrops, a Merkle-tree approach is far more efficient: the funder commits a single Merkle root, and each recipient claims their allocation by providing a Merkle proof. This reduces on-chain cost from O(n) to O(log n) per recipient.
Problem Statement
Projects wanting to airdrop tokens to thousands of Stellar addresses have no efficient on-chain mechanism in Finchippay.
Objectives
- Implement
create_airdrop, claim_airdrop, and cancel_airdrop functions.
- Implement Merkle proof verification using SHA-256 hashing.
- Write comprehensive tests with known Merkle trees.
Scope
- In scope: Merkle proof verification, airdrop create/claim/cancel, unit tests with known Merkle trees.
- Out of scope: off-chain Merkle tree generation tooling (future issue), NFT airdrops.
Detailed Implementation Requirements
- Data structures: Define
MerkleAirdrop struct with fields id, funder, token, merkle_root: BytesN<32>, total_amount: i128, claimed_amount: i128, expiration_ledger: u32, cancelled: bool.
- Storage keys: Add
DataKey::Airdrop(u32), DataKey::AirdropCount, DataKey::AirdropClaimed(u32, Address).
- Merkle verification: Implement a pure
verify_merkle_proof(leaf: BytesN<32>, proof: Vec<BytesN<32>>, root: BytesN<32>) -> bool helper. Use iterative SHA-256 hashing: start with leaf, then for each proof element, hash the concatenation of the current hash and the proof element (sorted — hash lower first as per standard Merkle tree conventions).
- Implement
create_airdrop(env, token, funder, merkle_root, total_amount, expiration_ledger): Transfer total amount from funder to contract. Store airdrop. Emit event.
- Implement
claim_airdrop(env, id, recipient, amount, proof): Verify proof for hash(recipient, amount). Check not already claimed and airdrop not cancelled. Mark claimed. Transfer tokens. Emit event.
- Implement
cancel_airdrop(env, id, funder): Only callable by funder after expiration. Refund unclaimed amount. Emit event.
- Tests: Single-recipient tree, 3-recipient tree, invalid proof rejection, double-claim rejection, cancel after expiration, cancel before expiration (should fail).
Expected Architecture
contracts/finchippay-contract/src/lib.rs
+ MerkleAirdrop struct
+ verify_merkle_proof() helper
+ DataKey::Airdrop*, DataKey::AirdropClaimed
+ create_airdrop(), claim_airdrop(), cancel_airdrop()
+ 6+ new tests
Acceptance Criteria
Issue #4 — Merkle-Tree Airdrop Contract Extension
Labels:
contractfeaturesorobanairdropSummary
Add a Merkle-tree-based airdrop mechanism to
FinchippayContractenabling gas-efficient token distributions to thousands of recipients.Background
The current
batch_sendfunction (lib.rsline ~590) is limited to 50 recipients per call and requires the sender to pay gas for every transfer inline. For large-scale airdrops, a Merkle-tree approach is far more efficient: the funder commits a single Merkle root, and each recipient claims their allocation by providing a Merkle proof. This reduces on-chain cost from O(n) to O(log n) per recipient.Problem Statement
Projects wanting to airdrop tokens to thousands of Stellar addresses have no efficient on-chain mechanism in Finchippay.
Objectives
create_airdrop,claim_airdrop, andcancel_airdropfunctions.Scope
Detailed Implementation Requirements
MerkleAirdropstruct with fieldsid,funder,token,merkle_root: BytesN<32>,total_amount: i128,claimed_amount: i128,expiration_ledger: u32,cancelled: bool.DataKey::Airdrop(u32),DataKey::AirdropCount,DataKey::AirdropClaimed(u32, Address).verify_merkle_proof(leaf: BytesN<32>, proof: Vec<BytesN<32>>, root: BytesN<32>) -> boolhelper. Use iterative SHA-256 hashing: start withleaf, then for each proof element, hash the concatenation of the current hash and the proof element (sorted — hash lower first as per standard Merkle tree conventions).create_airdrop(env, token, funder, merkle_root, total_amount, expiration_ledger): Transfer total amount from funder to contract. Store airdrop. Emit event.claim_airdrop(env, id, recipient, amount, proof): Verify proof forhash(recipient, amount). Check not already claimed and airdrop not cancelled. Mark claimed. Transfer tokens. Emit event.cancel_airdrop(env, id, funder): Only callable by funder after expiration. Refund unclaimed amount. Emit event.Expected Architecture
Acceptance Criteria
cargo testpasses with ≥6 new airdrop-specific tests.