Airdrop Contract Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Airdrop Contract Development
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Airdrop Contract

Classic mistake — implement airdrop via loop with on-chain transfer to each address. For 10k recipients that's 10k transactions, tens of thousands dollars gas and several hours of work. Correct approach — Merkle distributor: once publish Merkle root on-chain, each recipient claims their tokens themselves, paying for their own inclusion.

Merkle Distributor: Standard Implementation

Addresses and amounts list → build off-chain Merkle tree → publish root on-chain → user provides proof of their leaf → contract verifies and transfers tokens.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

contract MerkleAirdrop {
    IERC20 public immutable token;
    bytes32 public immutable merkleRoot;
    
    mapping(uint256 => uint256) private claimedBitMap;
    
    constructor(address _token, bytes32 _merkleRoot) {
        token = IERC20(_token);
        merkleRoot = _merkleRoot;
    }
    
    function isClaimed(uint256 index) public view returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex  = index % 256;
        uint256 claimedWord      = claimedBitMap[claimedWordIndex];
        uint256 mask             = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }
    
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof
    ) external {
        require(!isClaimed(index), "Already claimed");
        
        bytes32 leaf = keccak256(bytes.concat(
            keccak256(abi.encode(index, account, amount))
        )); // Double-hash against second preimage attack
        
        require(
            MerkleProof.verify(merkleProof, merkleRoot, leaf),
            "Invalid proof"
        );
        
        _setClaimed(index);
        require(token.transfer(account, amount), "Transfer failed");
        emit Claimed(index, account, amount);
    }
}

Why double-hash leaf. Without it — second preimage attack: attacker can substitute leaf with data matching intermediate node in tree. OpenZeppelin uses keccak256(bytes.concat(keccak256(abi.encode(...)))) exactly for this reason.

Bit packing for claimed. Instead of mapping(address => bool) use bit array: 256 statuses in one uint256 slot. SLOAD gas savings significant with large claim numbers.

Merkle Tree Generation Off-Chain

import { StandardMerkleTree } from "@openzeppelin/merkle-tree"

// List [index, address, amount]
const values = [
  [0, "0xAddress1...", ethers.parseEther("100")],
  [1, "0xAddress2...", ethers.parseEther("250")],
  // ...thousands of entries
]

const tree = StandardMerkleTree.of(values, ["uint256", "address", "uint256"])

console.log("Merkle Root:", tree.root) // → deploy to contract

// Proof for specific address
for (const [i, v] of tree.entries()) {
  if (v[1] === "0xAddress1...") {
    const proof = tree.getProof(i)
    // proof — array of bytes32, needed by user for claim
  }
}

// Save entire tree for proof distribution via API
import fs from "fs"
fs.writeFileSync("tree.json", JSON.stringify(tree.dump()))

Proofs distributed via simple API: GET /proof?address=0x... → returns { index, amount, proof[] }. User inserts this data in UI and calls claim.

Additional Considerations

Expiry. Add deadline after which unclaimed tokens return to owner. Otherwise tokens locked forever.

uint256 public immutable claimDeadline;

function claim(...) external {
    require(block.timestamp <= claimDeadline, "Airdrop expired");
    // ...
}

function recoverExpired() external onlyOwner {
    require(block.timestamp > claimDeadline, "Not expired yet");
    token.transfer(owner(), token.balanceOf(address(this)));
}

Vesting airdrop. If tokens shouldn't be available immediately — linear vesting directly in distributor contract: claim → tokens to vesting schedule → claim vested over time.

Gas for users. On mainnet claim costs ~$2-10. Consider deploying on L2 (Arbitrum, Base, Optimism) — claim costs cents. Or gasless claim via ERC-2771 meta-transactions (user signs, relayer pays gas).

Workflow

Addresses and amounts list → off-chain tree generation → deploy contract with root → transfer tokens to contract → API for proofs → UI for claim. Standard scope: 1-2 weeks including testing and deployment.