Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x0042c8087910d4F6aED017932361d5A933ae507A
Balance 0 ETH
Nonce 1
Code Size 4181 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

4181 bytes
0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80638da5cb5b116100715780638da5cb5b1461026357806393a10984146102ad57806394f3f81d146102cb5780639e34070f1461030f578063ecda10f514610355578063fc0c546a14610373576100a9565b806305ab421d146100ae57806324ba5884146100fc5780632e7ba6ef146101545780632eb4a7ab1461020157806335b281531461021f575b600080fd5b6100fa600480360360408110156100c457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103bd565b005b61013e6004803603602081101561011257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106bb565b6040518082815260200191505060405180910390f35b6101ff6004803603608081101561016a57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156101bb57600080fd5b8201836020820111156101cd57600080fd5b803590602001918460208302840111640100000000831117156101ef57600080fd5b90919293919293905050506106d3565b005b610209610a35565b6040518082815260200191505060405180910390f35b6102616004803603602081101561023557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a59565b005b61026b610b9a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102b5610bbe565b6040518082815260200191505060405180910390f35b61030d600480360360208110156102e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bc5565b005b61033b6004803603602081101561032557600080fd5b8101908080359060200190929190505050610d06565b604051808215151515815260200191505060405180910390f35b61035d610d58565b6040518082815260200191505060405180910390f35b61037b610d7c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61048e60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054146104893373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000b5ed650ef207e051453b68a2138d7cb67cc85e4173ffffffffffffffffffffffffffffffffffffffff16146104817f0000000000000000000000000000000000000000000000000000000066cf59876276a700610da0565b421015610e06565b610e13565b6104e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180610fae602b913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610569576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180610fd96021913960400191505060405180910390fd5b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d602081101561063a57600080fd5b8101908080519060200190929190505050507f09bd3894cb7ab22415416dac0fecc519855a4b0842f1c9115e562ef557ab577b8282604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15050565b60006020528060005260406000206000915090505481565b6106dc85610d06565b15610732576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180610ffa6026913960400191505060405180910390fd5b6000858585604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018281526020019350505050604051602081830303815290604052805190602001209050610808838380806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050507f606b32002c8d54f679edfb8bee17353a357ffbe5cdb97174d5dcd51bc9cf0b9783610e20565b61087a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4d65726b6c654469737472696275746f722f696e76616c69642d70726f6f660081525060200191505060405180910390fd5b61088386610ed8565b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86866040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561092a57600080fd5b505af115801561093e573d6000803e3d6000fd5b505050506040513d602081101561095457600080fd5b81019080805190602001909291905050506109ba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180610f2f6021913960400191505060405180910390fd5b7f4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed026868686604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a1505050505050565b7f606b32002c8d54f679edfb8bee17353a357ffbe5cdb97174d5dcd51bc9cf0b9781565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610af0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f7f602f913960400191505060405180910390fd5b60016000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f599a298163e1678bb1c676052a8930bf0b8a1261ed6e01b8a2391e55f700010281604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b7f000000000000000000000000b5ed650ef207e051453b68a2138d7cb67cc85e4181565b6276a70081565b60016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414610c5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f7f602f913960400191505060405180910390fd5b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f8834a87e641e9716be4f34527af5d23e11624f1ddeefede6ad75a9acfc31b90381604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b6000806101008381610d1457fe5b04905060006101008481610d2457fe5b0690506000600160008481526020019081526020016000205490506000826001901b90508081831614945050505050919050565b7f0000000000000000000000000000000000000000000000000000000066cf598781565b7f0000000000000000000000006243d8cea23066d098a15582d81a598b4e8391f481565b6000828284019150811015610e00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f815260200180610f50602f913960400191505060405180910390fd5b92915050565b6000818316905092915050565b6000818317905092915050565b60008082905060008090505b8551811015610eca576000868281518110610e4357fe5b60200260200101519050808311610e8a5782816040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209250610ebc565b808360405160200180838152602001828152602001925050506040516020818303038152906040528051906020012092505b508080600101915050610e2c565b508381149150509392505050565b60006101008281610ee557fe5b04905060006101008381610ef557fe5b069050806001901b600160008481526020019081526020016000205417600160008481526020019081526020016000208190555050505056fe4d65726b6c654469737472696275746f722f7472616e736665722d6661696c65644d65726b6c654469737472696275746f72466163746f72792f6164642d75696e742d75696e742d6f766572666c6f774d65726b6c654469737472696275746f72466163746f72792f6163636f756e742d6e6f742d617574686f72697a65644d65726b6c654469737472696275746f72466163746f72792f63616e6e6f742d73656e642d746f6b656e734d65726b6c654469737472696275746f72466163746f72792f6e756c6c2d6473744d65726b6c654469737472696275746f722f64726f702d616c72656164792d636c61696d6564a2646970667358221220528f15fdf759afd9c80c6e566c7284f43679cac830e946499c4a21591ba9ff2764736f6c63430006070033

Verified Source Code Partial Match

Compiler: v0.6.7+commit.b8d736ae EVM: istanbul Optimization: No
MerkleDistributor.sol 371 lines
/**
 *Submitted for verification at Etherscan.io on 2021-04-03
*/

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.6.7;

// Allows anyone to claim a token if they exist in a merkle root
abstract contract IMerkleDistributor {
    // Time from the moment this contract is deployed and until the owner can withdraw leftover tokens
    uint256 public constant timelapseUntilWithdrawWindow = 90 days;

    // Returns the address of the token distributed by this contract
    function token() virtual external view returns (address);
    // Returns the merkle root of the merkle tree containing account balances available to claim
    function merkleRoot() virtual external view returns (bytes32);
    // Returns the timestamp when this contract was deployed
    function deploymentTime() virtual external view returns (uint256);
    // Returns the address for the owner of this contract
    function owner() virtual external view returns (address);
    // Returns true if the index has been marked claimed
    function isClaimed(uint256 index) virtual external view returns (bool);
    // Send tokens to an address without that address claiming them
    function sendTokens(address dst, uint256 tokenAmount) virtual external;
    // Claim the given amount of the token to the given address. Reverts if the inputs are invalid
    function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) virtual external;

    // This event is triggered whenever an address is added to the set of authed addresses
    event AddAuthorization(address account);
    // This event is triggered whenever an address is removed from the set of authed addresses
    event RemoveAuthorization(address account);
    // This event is triggered whenever a call to #claim succeeds
    event Claimed(uint256 index, address account, uint256 amount);
    // This event is triggered whenever some tokens are sent to an address without that address claiming them
    event SendTokens(address dst, uint256 tokenAmount);
}

/**
 * @dev These functions deal with verification of Merkle trees (hash trees),
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        bytes32 computedHash = leaf;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }
}

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract MerkleDistributor is IMerkleDistributor {
    // --- Auth ---
    mapping (address => uint) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) virtual external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) virtual external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MerkleDistributorFactory/account-not-authorized");
        _;
    }
    /*
    * @notify Checks whether an address can send tokens out of this contract
    */
    modifier canSendTokens {
        require(
          either(authorizedAccounts[msg.sender] == 1, both(owner == msg.sender, now >= addition(deploymentTime, timelapseUntilWithdrawWindow))),
          "MerkleDistributorFactory/cannot-send-tokens"
        );
        _;
    }

    // The token being distributed
    address public immutable override token;
    // The owner of this contract
    address public immutable override owner;
    // The merkle root of all addresses that get a distribution
    bytes32 public immutable override merkleRoot;
    // Timestamp when this contract was deployed
    uint256 public immutable override deploymentTime;

    // This is a packed array of booleans
    mapping(uint256 => uint256) private claimedBitMap;

    constructor(address token_, bytes32 merkleRoot_) public {
        authorizedAccounts[msg.sender] = 1;
        owner                          = msg.sender;
        token                          = token_;
        merkleRoot                     = merkleRoot_;
        deploymentTime                 = now;

        emit AddAuthorization(msg.sender);
    }

    // --- Math ---
    function addition(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "MerkleDistributorFactory/add-uint-uint-overflow");
    }

    // --- Boolean Logic ---
    function either(bool x, bool y) internal pure returns (bool z) {
        assembly{ z := or(x, y)}
    }
    function both(bool x, bool y) internal pure returns (bool z) {
        assembly{ z := and(x, y)}
    }

    // --- Administration ---
    /*
    * @notice Send tokens to an authorized address
    * @param dst The address to send tokens to
    * @param tokenAmount The amount of tokens to send
    */
    function sendTokens(address dst, uint256 tokenAmount) external override canSendTokens {
        require(dst != address(0), "MerkleDistributorFactory/null-dst");
        IERC20(token).transfer(dst, tokenAmount);
        emit SendTokens(dst, tokenAmount);
    }

    /*
    * @notice View function returning whether an address has already claimed their tokens
    * @param index The position of the address inside the merkle tree
    */
    function isClaimed(uint256 index) public view override returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMap[claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }
    /*
    * @notice Mark an address as having claimed their distribution
    * @param index The position of the address inside the merkle tree
    */
    function _setClaimed(uint256 index) private {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
    }
    /*
    * @notice Claim your distribution
    * @param index The position of the address inside the merkle tree
    * @param account The actual address from the tree
    * @param amount The amount being distributed
    * @param merkleProof The merkle path used to prove that the address is in the tree and can claim amount tokens
    */
    function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override {
        require(!isClaimed(index), 'MerkleDistributor/drop-already-claimed');

        // Verify the merkle proof
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDistributor/invalid-proof');

        // Mark it claimed and send the token
        _setClaimed(index);
        require(IERC20(token).transfer(account, amount), 'MerkleDistributor/transfer-failed');

        emit Claimed(index, account, amount);
    }
}

contract MerkleDistributorFactory {
    // --- Auth ---
    mapping (address => uint) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) virtual external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) virtual external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MerkleDistributorFactory/account-not-authorized");
        _;
    }

    // --- Variables ---
    // Number of distributors created
    uint256 public nonce;
    // The token that's being distributed by every merkle distributor
    address public distributedToken;
    // Mapping of ID => distributor address
    mapping(uint256 => address) public distributors;
    // Tokens left to distribute to every distributor
    mapping(uint256 => uint256) public tokensToDistribute;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DeployDistributor(uint256 id, address distributor, uint256 tokenAmount);
    event SendTokensToDistributor(uint256 id);

    constructor(address distributedToken_) public {
        require(distributedToken_ != address(0), "MerkleDistributorFactory/null-distributed-token");

        authorizedAccounts[msg.sender] = 1;
        distributedToken               = distributedToken_;

        emit AddAuthorization(msg.sender);
    }

    // --- Math ---
    function addition(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "MerkleDistributorFactory/add-uint-uint-overflow");
    }

    // --- Core Logic ---
    /*
    * @notice Deploy a new merkle distributor
    * @param merkleRoot The merkle root used in the distributor
    */
    function deployDistributor(bytes32 merkleRoot, uint256 tokenAmount) external isAuthorized {
        require(tokenAmount > 0, "MerkleDistributorFactory/null-token-amount");
        nonce                     = addition(nonce, 1);
        address newDistributor    = address(new MerkleDistributor(distributedToken, merkleRoot));
        distributors[nonce]       = newDistributor;
        tokensToDistribute[nonce] = tokenAmount;
        emit DeployDistributor(nonce, newDistributor, tokenAmount);
    }
    /*
    * @notice Send tokens to a distributor
    * @param nonce The nonce/id of the distributor to send tokens to
    */
    function sendTokensToDistributor(uint256 id) external isAuthorized {
        require(tokensToDistribute[id] > 0, "MerkleDistributorFactory/nothing-to-send");
        uint256 tokensToSend = tokensToDistribute[id];
        tokensToDistribute[id] = 0;
        IERC20(distributedToken).transfer(distributors[id], tokensToSend);
        emit SendTokensToDistributor(id);
    }
    /*
    * @notice Sent distributedToken tokens out of this contract and to a custom destination
    * @param dst The address that will receive tokens
    * @param tokenAmount The token amount to send
    */
    function sendTokensToCustom(address dst, uint256 tokenAmount) external isAuthorized {
        require(dst != address(0), "MerkleDistributorFactory/null-dst");
        IERC20(distributedToken).transfer(dst, tokenAmount);
    }
    /*
    * @notice This contract gives up on being an authorized address inside a specific distributor contract
    */
    function dropDistributorAuth(uint256 id) external isAuthorized {
        MerkleDistributor(distributors[id]).removeAuthorization(address(this));
    }
    /*
    * @notice Send tokens from a distributor contract to this contract
    */
    function getBackTokensFromDistributor(uint256 id, uint256 tokenAmount) external isAuthorized {
        MerkleDistributor(distributors[id]).sendTokens(address(this), tokenAmount);
    }
}

Read Contract

authorizedAccounts 0x24ba5884 → uint256
deploymentTime 0xecda10f5 → uint256
isClaimed 0x9e34070f → bool
merkleRoot 0x2eb4a7ab → bytes32
owner 0x8da5cb5b → address
timelapseUntilWithdrawWindow 0x93a10984 → uint256
token 0xfc0c546a → address

Write Contract 4 functions

These functions modify contract state and require a wallet transaction to execute.

addAuthorization 0x35b28153
address account
claim 0x2e7ba6ef
uint256 index
address account
uint256 amount
bytes32[] merkleProof
removeAuthorization 0x94f3f81d
address account
sendTokens 0x05ab421d
address dst
uint256 tokenAmount

Recent Transactions

No transactions found for this address