Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x885D90A424f87D362C9369C0F3d9A2d28AF495F4
Balance 0 ETH
Nonce 1
Code Size 4227 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

4227 bytes
0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806364c761171161007157806364c7611714610181578063711bc2cc146101895780639353b9df1461019c578063c2a395941461021a578063ecb16c951461022d578063f0f140561461024057600080fd5b80631287d614146100b957806315497d2c146100ce5780632eb4a7ab146101065780634e71d92d1461013b5780634f2bfe5b1461014357806361d027b31461016e575b600080fd5b6100cc6100c7366004610d77565b610253565b005b6100f16100dc366004610e56565b60046020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b61012d7fbc39affb2a6f4c1e539660ab71ae1554d613a42413e154a6223dd7c868432e5881565b6040519081526020016100fd565b6100cc6103df565b600254610156906001600160a01b031681565b6040516001600160a01b0390911681526020016100fd565b600554610156906001600160a01b031681565b6100cc61051f565b6100cc610197366004610e71565b61081d565b6101e56101aa366004610e56565b600360205260009081526040902080546001909101546001600160401b0380831692600160401b8104821692600160801b9091049091169084565b6040516100fd94939291906001600160401b039485168152928416602084015292166040820152606081019190915260800190565b61012d610228366004610e8a565b610a90565b600154610156906001600160a01b031681565b61012d61024e366004610ecd565b610be4565b336000908152600360205260409020546001600160401b03161561028a5760405163e0e70c4960e01b815260040160405180910390fd5b6040516bffffffffffffffffffffffff193360601b166020820152603481018290526102d090839060540160405160208183030381529060405280519060200120610c83565b6102fc57338183604051630bcc64c960e21b81526004016102f393929190610eef565b60405180910390fd5b60408051608081018252426001600160401b0381168083526000602084015292820192909252606081016103326103e885610f62565b9052336000818152600360209081526040918290208451815486840151878601516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026fffffffffffffffffffffffffffffffff1990941691909416179190911716178155606090940151600190940193909355519081527fab67d7ea3f6d1612bda63a01384162c8ee0decb1b35cab6c6ff10dbe0e7d57e0910160405180910390a1505050565b6000546001146104015760405162461bcd60e51b81526004016102f390610f84565b600260009081556104123382610a90565b9050806000036104355760405163be7538fb60e01b815260040160405180910390fd5b3360008181526003602052604090819020805467ffffffffffffffff60801b1916600160801b426001600160401b031602179055600154905163a9059cbb60e01b81526004810192909252602482018390526001600160a01b03169063a9059cbb906044016020604051808303816000875af11580156104b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104dd9190610fa8565b5060408051338152602081018390527f896e034966eaaf1adc54acc0f257056febbd300c9e47182cf761982cf1f5e430910160405180910390a1506001600055565b6000546001146105415760405162461bcd60e51b81526004016102f390610f84565b600260009081556105523382610a90565b9050806000036105755760405163be7538fb60e01b815260040160405180910390fd5b3360009081526003602052604081208054909142916105bc906105a1906001600160401b031684610fd1565b6001600160401b0316670de0b6b3a764000062015180610cb0565b905060006105ce846001015483610be4565b84600101546105dd9190610ff8565b905060006106396813c9647e25a9940000841161062057610609846351a673656509184e72a000610cb0565b61061b90670de0b6b3a7640000610ff8565b61062a565b670d2f13f7789f00005b83670de0b6b3a7640000610cb0565b905060006106478284610ff8565b6001549091506001600160a01b031663a9059cbb336106668a8561100b565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156106b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d59190610fa8565b5060015460055460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810185905291169063a9059cbb906044016020604051808303816000875af115801561072b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074f9190610fa8565b50855477ffffffffffffffffffffffffffffffff00000000000000001916600160801b6001600160401b03871690810267ffffffffffffffff60401b191691909117600160401b9190910217865560408051338152602081018990527f896e034966eaaf1adc54acc0f257056febbd300c9e47182cf761982cf1f5e430910160405180910390a160408051338152602081018390527f5367ccf79b9a526d66835ee11189c8c710ba3ef031629c51977139111d90b953910160405180910390a1505060016000555050505050565b60005460011461083f5760405162461bcd60e51b81526004016102f390610f84565b600260009081553381526004602052604090205460ff16151560010361087857604051637e2f954760e01b815260040160405180910390fd5b6000610885336000610a90565b9050806000036108a85760405163be7538fb60e01b815260040160405180910390fd5b6108b6426305a39a8061100b565b8210156108d65760405163d6cf6c6760e01b815260040160405180910390fd5b336000908152600360205260408120805490914291610902906105a1906001600160401b031684610fd1565b90506000610914846001015483610be4565b84600101546109239190610ff8565b60015460025460405163095ea7b360e01b81526001600160a01b03918216600482015260248101849052929350169063095ea7b3906044016020604051808303816000875af115801561097a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099e9190610fa8565b50600254604051633e173b2960e01b815233600482015260248101839052604481018890526001600160a01b0390911690633e173b2990606401600060405180830381600087803b1580156109f257600080fd5b505af1158015610a06573d6000803e3d6000fd5b5050336000818152600460209081526040918290208054600160ff19909116179055885467ffffffffffffffff60401b1916600160401b6001600160401b038a1602178955815192835282018590527f8bcae1a205c9e844b0492205ba6ad2b480293ba662b8d7105268451eb7b78d2f935001905060405180910390a15050600160005550505050565b6001600160a01b0382166000908152600360209081526040808320815160808101835281546001600160401b03808216808452600160401b8304821696840196909652600160801b909104169281019290925260010154606082015290808303610b0d5760405163be7538fb60e01b815260040160405180910390fd5b60208201516001600160401b031615610b395760405163bc21cd9360e01b815260040160405180910390fd5b60006001600160401b03851615610b505784610b52565b425b9050816001600160401b0316816001600160401b03161015610b875760405163b7d0949760e01b815260040160405180910390fd5b6000610b966105a18484610fd1565b90506000610bad8560400151846105a19190610fd1565b6060860151909150610bc38161024e8486610ff8565b610bcd8285610be4565b610bd79190610ff8565b9998505050505050505050565b6000683b5c2d7a70fcbc00008210610bfd575081610c7d565b610c24565b808202831515821583830485141716610c1a57600080fd5b9290920492915050565b610c7a68056bc75e2d6310000084670de0b6b3a76400006002610c73670de0b6b3a764000088610c6e680bdf3c4bb0328c0000670de0b6b3a7640000671bc16d674ec80000610c02565b610c02565b0a04610c02565b90505b92915050565b6000610c7a837fbc39affb2a6f4c1e539660ab71ae1554d613a42413e154a6223dd7c868432e5884610ccf565b828202811515841585830485141716610cc857600080fd5b0492915050565b600082610cdc8584610ce5565b14949350505050565b600081815b8451811015610d2a57610d1682868381518110610d0957610d0961101e565b6020026020010151610d32565b915080610d2281611034565b915050610cea565b509392505050565b6000818310610d4e576000828152602084905260409020610c7a565b6000838152602083905260409020610c7a565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610d8a57600080fd5b82356001600160401b0380821115610da157600080fd5b818501915085601f830112610db557600080fd5b8135602082821115610dc957610dc9610d61565b8160051b604051601f19603f83011681018181108682111715610dee57610dee610d61565b604052928352818301935084810182019289841115610e0c57600080fd5b948201945b83861015610e2a57853585529482019493820193610e11565b9997909101359750505050505050565b80356001600160a01b0381168114610e5157600080fd5b919050565b600060208284031215610e6857600080fd5b610c7a82610e3a565b600060208284031215610e8357600080fd5b5035919050565b60008060408385031215610e9d57600080fd5b610ea683610e3a565b915060208301356001600160401b0381168114610ec257600080fd5b809150509250929050565b60008060408385031215610ee057600080fd5b50508035926020909101359150565b6001600160a01b038416815260208082018490526060604083018190528351908301819052600091848101916080850190845b81811015610f3e57845183529383019391830191600101610f22565b509098975050505050505050565b634e487b7160e01b600052601160045260246000fd5b600082610f7f57634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b600060208284031215610fba57600080fd5b81518015158114610fca57600080fd5b9392505050565b6001600160401b03828116828216039080821115610ff157610ff1610f4c565b5092915050565b81810381811115610c7d57610c7d610f4c565b80820180821115610c7d57610c7d610f4c565b634e487b7160e01b600052603260045260246000fd5b60006001820161104657611046610f4c565b506001019056fea264697066735822122017a5850431d77d69fe840581eb6e10d56c3dfc7c566613a013d65026447d395364736f6c63430008110033

Verified Source Code Full Match

Compiler: v0.8.17+commit.8df45f5f EVM: london Optimization: Yes (200 runs)
BaoDistribution.sol 819 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
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) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
     * consuming from one or the other at each step according to the instructions given by
     * `proofFlags`.
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}


/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}


/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLibrary {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        assembly {
            // Start off with z at 1.
            z := 1

            // Used below to help find a nearby power of 2.
            let y := x

            // Find the lowest power of 2 that is at least sqrt(x).
            if iszero(lt(y, 0x100000000000000000000000000000000)) {
                y := shr(128, y) // Like dividing by 2 ** 128.
                z := shl(64, z) // Like multiplying by 2 ** 64.
            }
            if iszero(lt(y, 0x10000000000000000)) {
                y := shr(64, y) // Like dividing by 2 ** 64.
                z := shl(32, z) // Like multiplying by 2 ** 32.
            }
            if iszero(lt(y, 0x100000000)) {
                y := shr(32, y) // Like dividing by 2 ** 32.
                z := shl(16, z) // Like multiplying by 2 ** 16.
            }
            if iszero(lt(y, 0x10000)) {
                y := shr(16, y) // Like dividing by 2 ** 16.
                z := shl(8, z) // Like multiplying by 2 ** 8.
            }
            if iszero(lt(y, 0x100)) {
                y := shr(8, y) // Like dividing by 2 ** 8.
                z := shl(4, z) // Like multiplying by 2 ** 4.
            }
            if iszero(lt(y, 0x10)) {
                y := shr(4, y) // Like dividing by 2 ** 4.
                z := shl(2, z) // Like multiplying by 2 ** 2.
            }
            if iszero(lt(y, 0x8)) {
                // Equivalent to 2 ** z.
                z := shl(1, z)
            }

            // Shifting right by 1 is like dividing by 2.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // Compute a rounded down version of z.
            let zRoundDown := div(x, z)

            // If zRoundDown is smaller, use it.
            if lt(zRoundDown, z) {
                z := zRoundDown
            }
        }
    }
}


interface IVotingEscrow {
    function create_lock_for(address _to, uint256 _value, uint256 _unlock_time) external;
}


contract BaoDistribution is ReentrancyGuard {

    // -------------------------------
    // VARIABLES
    // -------------------------------

    //BaoToken public baoToken;
    IERC20 public baoToken;
    IVotingEscrow public votingEscrow;
    mapping(address => DistInfo) public distributions;
    mapping(address => bool) public lockStatus;
    address public treasury;

    // -------------------------------
    // CONSTANTS
    // -------------------------------

    bytes32 public immutable merkleRoot;

    // -------------------------------
    // STRUCTS
    // -------------------------------

    struct DistInfo {
        uint64 dateStarted;
        uint64 dateEnded;
        uint64 lastClaim;
        uint256 amountOwedTotal;
    }

    // -------------------------------
    // EVENTS
    // -------------------------------

    event DistributionStarted(address _account);
    event TokensClaimed(address _account, uint256 _amount);
    event DistributionEnded(address _account, uint256 _amount);
    event DistributionLocked(address _account, uint256 _amount);

    // -------------------------------
    // CUSTOM ERRORS
    // -------------------------------

    error DistributionAlreadyStarted();
    error DistributionEndedEarly();
    error InvalidProof(address _account, uint256 _amount, bytes32[] _proof);
    error ZeroClaimable();
    error InvalidTimestamp();
    error outsideLockRange();
    error alreadyLocked();

    /**
     * Create a new BaoDistribution contract.
     *
     * @param _baoToken Token to distribute.
     * @param _votingEscrow vote escrow BAO contract
     * @param _merkleRoot Merkle root to verify accounts' inclusion and amount owed when starting their distribution.
     */
    constructor(address _baoToken, address _votingEscrow ,bytes32 _merkleRoot, address _treasury) {
        baoToken = IERC20(_baoToken);
        votingEscrow = IVotingEscrow(_votingEscrow);
        merkleRoot = _merkleRoot;
        treasury = _treasury;
    }

    // -------------------------------
    // PUBLIC FUNCTIONS
    // -------------------------------

    /**
     * Starts the distribution of BAO for msg.sender.
     *
     * @param _proof Merkle proof to verify msg.sender's inclusion and claimed amount.
     * @param _amount Amount of tokens msg.sender is owed. Used to generate the merkle tree leaf.
     */
    function startDistribution(bytes32[] memory _proof, uint256 _amount) external {
        if (distributions[msg.sender].dateStarted != 0) {
            revert DistributionAlreadyStarted();
        } else if (!verifyProof(_proof, keccak256(abi.encodePacked(msg.sender, _amount)))) {
            revert InvalidProof(msg.sender, _amount, _proof);
        }

        uint64 _now = uint64(block.timestamp);
        distributions[msg.sender] = DistInfo(
            _now,
            0,
            _now,
            _amount / 1000
        );
        emit DistributionStarted(msg.sender);
    }

    /**
     * Claim all tokens that have been accrued since msg.sender's last claim.
     */
    function claim() external nonReentrant {
        uint256 _claimable = claimable(msg.sender, 0);
        if (_claimable == 0) {
            revert ZeroClaimable();
        }

        // Update account's DistInfo
        distributions[msg.sender].lastClaim = uint64(block.timestamp);

        // Send account the tokens that they've accrued since their last claim.
        baoToken.transfer(msg.sender, _claimable);

        // Emit tokens claimed event for logging
        emit TokensClaimed(msg.sender, _claimable);
    }

    /**
     * Claim all tokens that have been accrued since msg.sender's last claim AND
     * the rest of the total locked amount owed immediately at a pre-defined slashed rate.
     *
     * Slash Rate:
     * days_since_start <= 365: (100 - .01369863013 * days_since_start)%
     * days_since_start > 365: 95%
     */
    function endDistribution() external nonReentrant {
        uint256 _claimable = claimable(msg.sender, 0);
        if (_claimable == 0) {
            revert ZeroClaimable();
        }

        DistInfo storage distInfo = distributions[msg.sender];
        uint64 timestamp = uint64(block.timestamp);

        uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.dateStarted), 1e18, 86400);

        // Calculate total tokens left in distribution after the above claim
        uint256 tokensLeft = distInfo.amountOwedTotal - distCurve(distInfo.amountOwedTotal, daysSinceStart);

        // Calculate slashed amount
        uint256 slash = FixedPointMathLibrary.mulDivDown(
            daysSinceStart > 365e18 ? 95e16 : 1e18 - FixedPointMathLibrary.mulDivDown(daysSinceStart, 1369863013, 1e13),
            tokensLeft,
            1e18
        );
        uint256 owed = tokensLeft - slash;

        // Account gets slashed for (slash / tokensLeft)% of their remaining distribution
        baoToken.transfer(msg.sender, owed + _claimable);
        // Protocol treasury receives slashed tokens
        baoToken.transfer(treasury, slash);

        // Update DistInfo storage for account to reflect the end of the account's distribution
        distInfo.lastClaim = timestamp;
        distInfo.dateEnded = timestamp;

        // Emit tokens claimed event for logging
        emit TokensClaimed(msg.sender, _claimable);
        // Emit distribution ended event for logging
        emit DistributionEnded(msg.sender, owed);
    }

    /**
     * Lock all tokens that have NOT been claimed since msg.sender's last claim
     *
     * The Lock into veBAO will be set at _time with this function in-line with length of distribution curve (minimum of 3 years)
     */
    function lockDistribution(uint256 _time) external nonReentrant {
        if (lockStatus[msg.sender] == true) {
            revert alreadyLocked();
        }
        uint256 _claimable = claimable(msg.sender, 0);
        if (_claimable == 0) {
            revert ZeroClaimable();
        }
        if (_time < block.timestamp + 94608000) {
            revert outsideLockRange();
        }

        DistInfo storage distInfo = distributions[msg.sender];
        uint64 timestamp = uint64(block.timestamp);

        uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.dateStarted), 1e18, 86400);

        // Calculate total tokens left in distribution after the above claim
        uint256 tokensLeft = distInfo.amountOwedTotal - distCurve(distInfo.amountOwedTotal, daysSinceStart);

        baoToken.approve(address(votingEscrow), tokensLeft);

        //lock tokensLeft for msg.sender for _time years (minimum of 3 years)
        votingEscrow.create_lock_for(msg.sender, tokensLeft, _time);

        lockStatus[msg.sender] = true;
        distInfo.dateEnded = timestamp;

        emit DistributionLocked(msg.sender, tokensLeft);
    }

    /**
     * Get how many tokens an account is able to claim at a given timestamp. 0 = now.
     * This function takes into account the date of the account's last claim, and returns the amount
     * of tokens they've accrued since.
     *
     * @param _account Account address to query.
     * @param _timestamp Timestamp to query.
     * @return c _account's claimable tokens, scaled by 1e18.
     */
    function claimable(address _account, uint64 _timestamp) public view returns (uint256 c) {
        DistInfo memory distInfo = distributions[_account];
        uint64 dateStarted = distInfo.dateStarted;
        if (dateStarted == 0) {
            revert ZeroClaimable();
        } else if (distInfo.dateEnded != 0) {
            revert DistributionEndedEarly();
        }

        uint64 timestamp = _timestamp == 0 ? uint64(block.timestamp) : _timestamp;
        if (timestamp < dateStarted) {
            revert InvalidTimestamp();
        }

        uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - dateStarted), 1e18, 86400);
        uint256 daysSinceClaim = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.lastClaim), 1e18, 86400);

        // Allow the account to claim all tokens accrued since the last time they've claimed.
        uint256 _total = distInfo.amountOwedTotal;
        c = distCurve(_total, daysSinceStart) - distCurve(_total, daysSinceStart - daysSinceClaim);
    }

    /**
     * Get the amount of tokens that would have been accrued along the distribution curve, assuming _daysSinceStart
     * days have passed and the account has never claimed.
     *
     * f(x) = 0 <= x <= 1095 : (2x/219)^2
     *
     * @param _amountOwedTotal Total amount of tokens owed, scaled by 1e18.
     * @param _daysSinceStart Time since the start of the distribution, scaled by 1e18.
     * @return _amount Amount of tokens accrued on the distribution curve, assuming the time passed is _daysSinceStart.
     */
    function distCurve(uint256 _amountOwedTotal, uint256 _daysSinceStart) public pure returns (uint256 _amount) {
        if (_daysSinceStart >= 1095e18) return _amountOwedTotal;

        assembly {
            // Solmate's mulDivDown function
            function mulDivDown(x, y, denominator) -> z {
                // Store x * y in z for now.
                z := mul(x, y)

                // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
                if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                    revert(0, 0)
                }

                // Divide z by the denominator.
                z := div(z, denominator)
            }

            // This is disgusting, but its more gas efficient than storing the results in `_amount` each time.
            _amount := mulDivDown( // Multiply `amountOwedTotal` by distribution curve result
                div( // Correct precision after exponent op (scale down by 1e20 instead of 1e18 to convert % to a proportion)
                    exp( // Raise result to the power of two
                        mulDivDown( // (2/219) * `_daysSinceStart`
                            mulDivDown(0x1BC16D674EC80000, 0xDE0B6B3A7640000, 0xBDF3C4BB0328C0000),
                            _daysSinceStart,
                            0xDE0B6B3A7640000
                        ),
                        2
                    ),
                    0xDE0B6B3A7640000
                ),
                _amountOwedTotal,
                0x56BC75E2D63100000
            )
        }
    }

    // -------------------------------
    // PRIVATE FUNCTIONS
    // -------------------------------

    /**
     * Verifies a merkle proof against the stored root.
     *
     * @param _proof Merkle proof.
     * @param _leaf Leaf to verify.
     * @return bool True if proof is valid, false if proof is invalid.
     */
    function verifyProof(bytes32[] memory _proof, bytes32 _leaf) private view returns (bool) {
        return MerkleProof.verify(_proof, merkleRoot, _leaf);
    }
}

Read Contract

baoToken 0xecb16c95 → address
claimable 0xc2a39594 → uint256
distCurve 0xf0f14056 → uint256
distributions 0x9353b9df → uint64, uint64, uint64, uint256
lockStatus 0x15497d2c → bool
merkleRoot 0x2eb4a7ab → bytes32
treasury 0x61d027b3 → address
votingEscrow 0x4f2bfe5b → address

Write Contract 4 functions

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

claim 0x4e71d92d
No parameters
endDistribution 0x64c76117
No parameters
lockDistribution 0x711bc2cc
uint256 _time
startDistribution 0x1287d614
bytes32[] _proof
uint256 _amount

Recent Transactions

No transactions found for this address