Contract 0x261bd83050579edea22d90a18fcc9dafd43d10e3 2

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x2e7096744fd1cc7eedb29ac5f557b4962494fe78a7f54599c15d8c43c6715857Claim432607702023-05-28 19:36:0618 secs ago0xd8f7d2d62514895475afe0c7d75f31390dd40de4 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.061330089436 150.384824313
0x082b9780c9baf9a1150362a28d65a5c556d270a809f99ef67988a3451697e60bClaim432607602023-05-28 19:35:4440 secs ago0xb54a5205ee454f48ddfc23ca26a3836ba3dacc07 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.055316970404 132.463692388
0xd32f891bfe879853029e02a7dd147d47ced171e510a0d78b46f08524756db2f5Claim432607592023-05-28 19:35:4242 secs ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.058665622297 136.347651558
0xa78b363b71f7659417fe1cca2a897d6879a8c5b70156eb67ff72579345104d15Claim432607542023-05-28 19:35:3252 secs ago0x65bf36d6499a504100eb504f0719271f5c4174ec IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.05970317479 138.766818574
0x5d883786f0bd07ae5331dce4b9debe99e49156a94727f57f00386bbffb6e534dClaim432607532023-05-28 19:35:3054 secs ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.059007966441 137.143310381
0xe7f15aecc640dade385685ba3de4f47666531408f8033b2e4e601a82565d25a7Claim432607522023-05-28 19:35:2856 secs ago0x65bf36d6499a504100eb504f0719271f5c4174ec IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.056274506174 137.984190035
0xfb9eb172df943fe9cbbe2795588daa99d90ed506b34024c73a138b65a95fdb09Claim432607512023-05-28 19:35:241 min ago0xb54a5205ee454f48ddfc23ca26a3836ba3dacc07 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.054196846833 132.889802527
0x8f50b4fb3fd62886558d637e9dfab315b9d7c3944ff0f00fd82ce36b94e4ce3eClaim432607492023-05-28 19:35:201 min ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.05636682213 138.214614085
0xda85470e82ff54de58c1e4e1241dc08cba5e18acd1f873be3110e0438bcbb0a0Claim432607452023-05-28 19:35:121 min ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.055701979817 131.087848842
0xdd899173ecede7ed1748416da9e1a07a9aa39ba92f27d7b53c435d683a3e0cd6Claim432607442023-05-28 19:35:101 min ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.056557777518 135.402542773
0xfda16e35318891bb836c73b0a4c6a1674bd568017b8a5e74bd27725084e96ce4Claim432607422023-05-28 19:35:061 min ago0x65bf36d6499a504100eb504f0719271f5c4174ec IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.057691519923 134.08369243
0xd347fd9694f4f91279b946cd2caded135757a5b37606f95d0329eaa0bb1a96bfClaim432607402023-05-28 19:35:021 min ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.056617353399 135.549064973
0x83db23c95ffa8d893b602904b4ea66e66fd914afa4966c09378cb51d5305da4aClaim432607352023-05-28 19:34:501 min ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.058045301531 140.49755781
0x58e0d93c6f5bb594824469a6209e7a55b40c0e286ef3530e0caee6d579a04765Claim432607312023-05-28 19:34:421 min ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.05898802256 142.775249267
0x0cf98b95afb529acf8bff87f31d3fd82863e1518965fc0893842c640de880742Claim432607282023-05-28 19:34:361 min ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.060029723049 143.753123404
0x131b3ee7993c1ecebd759d68f3ae7fbe276d62ae9119f7d3073726c24dacf74cClaim432607212023-05-28 19:34:222 mins ago0xb54a5205ee454f48ddfc23ca26a3836ba3dacc07 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.061704444311 149.679060537
0xc459c03b823d7b9bbe95efa81bddb3af23862c3a8d73700167519a03303bfadfClaim432607032023-05-28 19:33:422 mins ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.059055895917 144.80837406
0x605893a4b911243fd17e49fc44e18465baf6a0ff54600f98b7046ee72040a5a5Claim432607022023-05-28 19:33:402 mins ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.060282689131 147.812190607
0x6b5c6f5c85a734870439d09db9a232d4d94bcc2d570cdd43ab9283586bb87c0bClaim432606952023-05-28 19:33:262 mins ago0xabe494eaa4ed80de8583c49183e9cbdadbc3b954 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.058513321748 143.482173636
0x0432be622e6c5d0ca19061dda5f099029cbb03d770e966371c6031c29b7b9259Claim432606902023-05-28 19:33:163 mins ago0xb54a5205ee454f48ddfc23ca26a3836ba3dacc07 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.059719780003 138.79767121
0x6997aa8b7c85f5638f41b9dce58eb2eab49c3c3741ae923d2c4e7bb53403fd9dClaim432606872023-05-28 19:33:083 mins ago0xd8f7d2d62514895475afe0c7d75f31390dd40de4 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.058210230527 135.257817266
0xfc7d3d7aa17c64f515d5ad05f48626889a367f222ffa1d49894ca934dcbb3131Claim432606842023-05-28 19:33:023 mins ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.057904593942 140.1149237
0x1c52d4a9134d8bb5d6b14cdbb26a4045466dc2f0da55d03f8a14f73514fc5f32Claim432606822023-05-28 19:32:583 mins ago0x4399fa85585f90da110d5ba150ff96c763bc0aba IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.0588408484 138.474795081
0xd4ed01834912b8ea379cd6a308cbdb26bd26ca7caea5c9cbb22a49f823c3543bClaim432606802023-05-28 19:32:543 mins ago0xb54a5205ee454f48ddfc23ca26a3836ba3dacc07 IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.060788957354 141.286539209
0xdd21e8c6874ddaa77e17c3eb94aaa7d334f12340af310a209c47fec84daaf0c6Claim432606792023-05-28 19:32:523 mins ago0x65bf36d6499a504100eb504f0719271f5c4174ec IN  0x261bd83050579edea22d90a18fcc9dafd43d10e30 MATIC0.059152715136 143.135071047
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WorldIDMultiAirdrop

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 5 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 2 of 5 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 3 of 5 : IWorldIDGroups.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title WorldID Interface with Groups
/// @author Worldcoin
/// @notice The interface to the proof verification for WorldID.
interface IWorldIDGroups {
    /// @notice Verifies a WorldID zero knowledge proof.
    /// @dev Note that a double-signaling check is not included here, and should be carried by the
    ///      caller.
    /// @dev It is highly recommended that the implementation is restricted to `view` if possible.
    ///
    /// @param groupId The group identifier for the group to verify a proof for.
    /// @param root The of the Merkle tree
    /// @param signalHash A keccak256 hash of the Semaphore signal
    /// @param nullifierHash The nullifier hash
    /// @param externalNullifierHash A keccak256 hash of the external nullifier
    /// @param proof The zero-knowledge proof
    ///
    /// @custom:reverts string If the `proof` is invalid.
    /// @custom:reverts NoSuchGroup If the provided `groupId` references a group that does not exist.
    function verifyProof(
        uint256 groupId,
        uint256 root,
        uint256 signalHash,
        uint256 nullifierHash,
        uint256 externalNullifierHash,
        uint256[8] calldata proof
    ) external;
}

File 4 of 5 : ByteHasher.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

library ByteHasher {
    /// @dev Creates a keccak256 hash of a bytestring.
    /// @param value The bytestring to hash
    /// @return The hash of the specified value
    /// @dev `>> 8` makes sure that the result is included in our field
    function hashToField(bytes memory value) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(value))) >> 8;
    }
}

File 5 of 5 : WorldIDMultiAirdrop.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {IWorldIDGroups} from "world-id-contracts/interfaces/IWorldIDGroups.sol";
import {ByteHasher} from "world-id-contracts/libraries/ByteHasher.sol";

/// @title World ID Multiple Airdrop Manager
/// @author Worldcoin
/// @notice Template contract for managing multiple airdrops to World ID members.
contract WorldIDMultiAirdrop {
    using ByteHasher for bytes;

    ///////////////////////////////////////////////////////////////////////////////
    ///                                  ERRORS                                ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Thrown when trying to create or update airdrop details without being the manager
    error Unauthorized();

    /// @notice Thrown when attempting to reuse a nullifier
    error InvalidNullifier();

    /// @notice Thrown when attempting to claim a non-existent airdrop
    error InvalidAirdrop();

    ///////////////////////////////////////////////////////////////////////////////
    ///                                  EVENTS                                ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Emitted when an airdrop is created
    /// @param airdropId The id of the airdrop
    /// @param airdrop The airdrop details
    event AirdropCreated(uint256 airdropId, Airdrop airdrop);

    /// @notice Emitted when an airdrop is successfully claimed
    /// @param receiver The address that received the airdrop
    event AirdropClaimed(uint256 indexed airdropId, address receiver);

    /// @notice Emitted when the airdropped amount is changed
    /// @param airdropId The id of the airdrop getting updated
    /// @param airdrop The new details for the airdrop
    event AirdropUpdated(uint256 indexed airdropId, Airdrop airdrop);

    ///////////////////////////////////////////////////////////////////////////////
    ///                                 STRUCTS                                ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Stores the details for a specific airdrop
    /// @param groupId The ID of the WorldIDRouter group that will be eligible to claim this airdrop
    /// @param token The ERC20 token that will be airdropped to eligible participants
    /// @param manager The address that manages this airdrop, which is allowed to update the airdrop details.
    /// @param holder The address holding the tokens that will be airdropped
    /// @param amount The amount of tokens that each participant will receive upon claiming
    struct Airdrop {
        uint256 groupId;
        ERC20 token;
        address manager;
        address holder;
        uint256 amount;
    }

    ///////////////////////////////////////////////////////////////////////////////
    ///                              CONFIG STORAGE                            ///
    //////////////////////////////////////////////////////////////////////////////

    /// @dev The WorldID router instance that will be used for managing groups and verifying proofs
    IWorldIDGroups internal immutable worldIdRouter;

    address internal immutable owner = msg.sender;

    /// @dev Whether a nullifier hash has been used already. Used to prevent double-signaling
    mapping(uint256 => bool) internal nullifierHashes;

    uint256 internal nextAirdropId = 1;
    mapping(uint256 => Airdrop) public getAirdrop;

    ///////////////////////////////////////////////////////////////////////////////
    ///                               CONSTRUCTOR                              ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Deploys a WorldIDAirdrop instance
    /// @param _worldIdRouter The WorldID router instance that will manage groups and verify proofs
    constructor(IWorldIDGroups _worldIdRouter) {
        worldIdRouter = _worldIdRouter;
    }

    /// @notice Create a new airdrop
    /// @param groupId The ID of the WorldIDRouter group that will be eligible to claim this airdrop
    /// @param token The ERC20 token that will be airdropped to eligible participants
    /// @param holder The address holding the tokens that will be airdropped
    /// @param amount The amount of tokens that each participant will receive upon claiming
    function createAirdrop(uint256 groupId, ERC20 token, address holder, uint256 amount) public {
        require(msg.sender == owner, "Sender must be owner");

        Airdrop memory airdrop = Airdrop({
            groupId: groupId,
            token: token,
            manager: msg.sender,
            holder: holder,
            amount: amount
        });

        getAirdrop[nextAirdropId] = airdrop;
        emit AirdropCreated(nextAirdropId, airdrop);

        ++nextAirdropId;
    }

    ///////////////////////////////////////////////////////////////////////////////
    ///                               CLAIM LOGIC                               ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Claim a given airdrop
    /// @param airdropId The id of the airdrop getting claimed
    /// @param receiver The address that will receive the tokens
    /// @param root The of the Merkle tree
    /// @param nullifierHash The nullifier for this proof, preventing double signaling
    /// @param proof The zero knowledge proof that demostrates the claimer is part of the WorldID group
    /// @dev hashToField function docs are in lib/world-id-contracts/src/libraries/ByteHasher.sol
    function claim(
        uint256 airdropId,
        address receiver,
        uint256 root,
        uint256 nullifierHash,
        uint256[8] calldata proof
    ) public {
        if (nullifierHashes[nullifierHash]) revert InvalidNullifier();

        Airdrop memory airdrop = getAirdrop[airdropId];
        if (airdropId == 0 || airdropId >= nextAirdropId) revert InvalidAirdrop();

        worldIdRouter.verifyProof(
            airdrop.groupId,
            root,
            abi.encodePacked(receiver).hashToField(),
            nullifierHash,
            abi.encodePacked(address(this), airdropId).hashToField(),
            proof
        );

        nullifierHashes[nullifierHash] = true;
        emit AirdropClaimed(airdropId, receiver);

        SafeTransferLib.safeTransferFrom(airdrop.token, airdrop.holder, receiver, airdrop.amount);
    }

    ///////////////////////////////////////////////////////////////////////////////
    ///                               CONFIG LOGIC                             ///
    //////////////////////////////////////////////////////////////////////////////

    /// @notice Update the details for a given airdrop, for addresses that haven't claimed already. Can only be called by the airdrop creator
    /// @param airdropId The id of the airdrop to update
    /// @param airdrop The new details for the airdrop
    function updateDetails(uint256 airdropId, Airdrop calldata airdrop) public {
        if (getAirdrop[airdropId].manager != msg.sender) revert Unauthorized();

        getAirdrop[airdropId] = airdrop;

        emit AirdropUpdated(airdropId, airdrop);
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@prb/test/=lib/prb-test/src/",
    "@zk-kit/=lib/zk-kit/packages/",
    "contracts-upgradeable/=lib/world-id-contracts/lib/openzeppelin-contracts-upgradeable/contracts/",
    "ds-test/=lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/world-id-contracts/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "prb-test/=lib/prb-test/src/",
    "semaphore/=lib/semaphore/",
    "solmate/=lib/solmate/src/",
    "src/=src/",
    "world-id-contracts/=lib/world-id-contracts/src/",
    "zk-kit/=lib/zk-kit/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000,
    "details": {
      "peephole": true,
      "inliner": true,
      "deduplicate": true,
      "cse": true,
      "yul": true
    }
  },
  "metadata": {
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IWorldIDGroups","name":"_worldIdRouter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAirdrop","type":"error"},{"inputs":[],"name":"InvalidNullifier","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"airdropId","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"AirdropClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"airdropId","type":"uint256"},{"components":[{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct WorldIDMultiAirdrop.Airdrop","name":"airdrop","type":"tuple"}],"name":"AirdropCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"airdropId","type":"uint256"},{"components":[{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct WorldIDMultiAirdrop.Airdrop","name":"airdrop","type":"tuple"}],"name":"AirdropUpdated","type":"event"},{"inputs":[{"internalType":"uint256","name":"airdropId","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"root","type":"uint256"},{"internalType":"uint256","name":"nullifierHash","type":"uint256"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"createAirdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getAirdrop","outputs":[{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"airdropId","type":"uint256"},{"components":[{"internalType":"uint256","name":"groupId","type":"uint256"},{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct WorldIDMultiAirdrop.Airdrop","name":"airdrop","type":"tuple"}],"name":"updateDetails","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523360a0526001805534801561001857600080fd5b50604051610bb2380380610bb283398101604081905261003791610048565b6001600160a01b0316608052610078565b60006020828403121561005a57600080fd5b81516001600160a01b038116811461007157600080fd5b9392505050565b60805160a051610b1561009d6000396000610143015260006104ff0152610b156000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063052dd94e14610051578063174e8f19146100f05780634abfa7f514610105578063a41e6ceb14610118575b600080fd5b6100ab61005f366004610795565b600260208190526000918252604090912080546001820154928201546003830154600490930154919373ffffffffffffffffffffffffffffffffffffffff908116939181169291169085565b6040805195865273ffffffffffffffffffffffffffffffffffffffff948516602087015292841685840152921660608401526080830191909152519081900360a00190f35b6101036100fe3660046107d3565b61012b565b005b61010361011336600461081b565b610309565b610103610126366004610871565b6103c3565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146101cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f53656e646572206d757374206265206f776e657200000000000000000000000060448201526064015b60405180910390fd5b6040805160a0808201835286825273ffffffffffffffffffffffffffffffffffffffff868116602080850191825233858701908152888416606080880191825260808089018b81526001805460009081526002808952908d90208c518155895181840180547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116928e16929092179055885192820180548216938d16939093179092558651600382018054909316908c16179091558251600490910155548b519081528a519681019690965295518716858b0152925186169084015251909316928101929092525191810191909152915190917f161a75881529a1e60e69cee62f03a3d68e230b9df3b9721293352bf8956f127e919081900360c00190a16001600081546102fe906108cd565b909155505050505050565b6000828152600260208190526040909120015473ffffffffffffffffffffffffffffffffffffffff16331461036a576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526002602052604090208190610384828261092c565b905050817f906c766311c9f795f65613a101faf1e64818a8b5bf0a7a05b010a09c743fd302826040516103b79190610a31565b60405180910390a25050565b60008281526020819052604090205460ff161561040c576040517f5d904cb200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260026020818152604092839020835160a08101855281548152600182015473ffffffffffffffffffffffffffffffffffffffff90811693820193909352928101548216938301939093526003830154166060820152600490910154608082015285158061048057506001548610155b156104b7576040517fe6dcad7700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b16602082015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691633bc778e3918790610544906034015b60405160208183030381529060405261067c565b6040517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166020820152603481018c9052889061058790605401610530565b886040518763ffffffff1660e01b81526004016105a996959493929190610aa1565b600060405180830381600087803b1580156105c357600080fd5b505af11580156105d7573d6000803e3d6000fd5b5050506000848152602081815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055905173ffffffffffffffffffffffffffffffffffffffff881681528892507fc56629d4035e5f0317cf45d4560cfd56c8ecdb7cec233123715086eb8d555999910160405180910390a2610674816020015182606001518784608001516106cf565b505050505050565b60006008826040516020016106919190610ad9565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120901c92915050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061078e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c454400000000000000000000000060448201526064016101c6565b5050505050565b6000602082840312156107a757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146107d057600080fd5b50565b600080600080608085870312156107e957600080fd5b8435935060208501356107fb816107ae565b9250604085013561080b816107ae565b9396929550929360600135925050565b60008082840360c081121561082f57600080fd5b8335925060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561086357600080fd5b506020830190509250929050565b600080600080600061018080878903121561088b57600080fd5b86359550602087013561089d816107ae565b945060408701359350606087013592508087018810156108bc57600080fd5b506080860190509295509295909350565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610925577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b81358155602082013561093e816107ae565b6001820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550604082013561098f816107ae565b6002820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555060608201356109e0816107ae565b6003820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905550608082013560048201555050565b8135815260a081016020830135610a47816107ae565b73ffffffffffffffffffffffffffffffffffffffff9081166020840152604084013590610a73826107ae565b9081166040840152606084013590610a8a826107ae565b166060830152608092830135929091019190915290565b60006101a0820190508782528660208301528560408301528460608301528360808301526101008360a0840137979650505050505050565b6000825160005b81811015610afa5760208186018101518583015201610ae0565b50600092019182525091905056fea164736f6c6343000813000a000000000000000000000000f7134ce138832c1456f2a91d64621ee90c2bddea

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f7134ce138832c1456f2a91d64621ee90c2bddea

-----Decoded View---------------
Arg [0] : _worldIdRouter (address): 0xf7134ce138832c1456f2a91d64621ee90c2bddea

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000f7134ce138832c1456f2a91d64621ee90c2bddea


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.