APE Price: $0.18 (-5.42%)

Contract

0xf5918F8cF2E6D9DfD78EF15a7cACfBa97a7e1e1b

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Block
From
To
Claim328061422026-01-25 4:43:5413 hrs ago1769316234IN
0xf5918F8c...97a7e1e1b
0 APE0.00758797101.68276
Claim328061332026-01-25 4:43:4113 hrs ago1769316221IN
0xf5918F8c...97a7e1e1b
0 APE0.00758675101.68276
Claim328061292026-01-25 4:43:2813 hrs ago1769316208IN
0xf5918F8c...97a7e1e1b
0 APE0.00758807101.68276
Claim328061152026-01-25 4:43:0713 hrs ago1769316187IN
0xf5918F8c...97a7e1e1b
0 APE0.00758431101.68276
Claim327379672026-01-23 18:22:5247 hrs ago1769192572IN
0xf5918F8c...97a7e1e1b
0 APE0.00932308101.68276
Claim327377132026-01-23 18:14:1647 hrs ago1769192056IN
0xf5918F8c...97a7e1e1b
0 APE0.00918734101.68276
Claim327376512026-01-23 18:12:2347 hrs ago1769191943IN
0xf5918F8c...97a7e1e1b
0 APE0.00374741101.68276
Claim327021062026-01-22 19:41:062 days ago1769110866IN
0xf5918F8c...97a7e1e1b
0 APE0.00758065101.68276
Claim327020992026-01-22 19:40:552 days ago1769110855IN
0xf5918F8c...97a7e1e1b
0 APE0.00716832101.68276
Claim327020952026-01-22 19:40:432 days ago1769110843IN
0xf5918F8c...97a7e1e1b
0 APE0.00911453101.68276
Claim325908942026-01-20 3:31:125 days ago1768879872IN
0xf5918F8c...97a7e1e1b
0 APE0.00758553101.68276
Claim325908862026-01-20 3:30:515 days ago1768879851IN
0xf5918F8c...97a7e1e1b
0 APE0.00758675101.68276
Claim325908512026-01-20 3:29:465 days ago1768879786IN
0xf5918F8c...97a7e1e1b
0 APE0.00918612101.68276
Claim325874682026-01-20 1:50:255 days ago1768873825IN
0xf5918F8c...97a7e1e1b
0 APE0.00758553101.68276
Claim325874592026-01-20 1:50:145 days ago1768873814IN
0xf5918F8c...97a7e1e1b
0 APE0.00758065101.68276
Claim325874482026-01-20 1:50:035 days ago1768873803IN
0xf5918F8c...97a7e1e1b
0 APE0.00758553101.68276
Claim325874372026-01-20 1:49:515 days ago1768873791IN
0xf5918F8c...97a7e1e1b
0 APE0.00758075101.68276
Claim325874242026-01-20 1:49:365 days ago1768873776IN
0xf5918F8c...97a7e1e1b
0 APE0.00716832101.68276
Claim325874182026-01-20 1:49:245 days ago1768873764IN
0xf5918F8c...97a7e1e1b
0 APE0.00911453101.68276
Claim325757482026-01-19 17:16:276 days ago1768842987IN
0xf5918F8c...97a7e1e1b
0 APE0.00918612101.68276
Claim325010012026-01-17 19:49:217 days ago1768679361IN
0xf5918F8c...97a7e1e1b
0 APE0.00758431101.68276
Claim324671062026-01-17 1:05:288 days ago1768611928IN
0xf5918F8c...97a7e1e1b
0 APE0.00932308101.68276
Claim321767642026-01-09 3:01:5716 days ago1767927717IN
0xf5918F8c...97a7e1e1b
0 APE0.00751893101.68276
Claim321767612026-01-09 3:01:4516 days ago1767927705IN
0xf5918F8c...97a7e1e1b
0 APE0.00932186101.68276
Claim321695412026-01-08 22:55:4016 days ago1767912940IN
0xf5918F8c...97a7e1e1b
0 APE0.0092577101.68276
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GangVesting

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
No with 200 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import "@solady/src/auth/Ownable.sol";
import "@solady/src/utils/SafeTransferLib.sol";
import "@solady/src/utils/MerkleProofLib.sol";

/// @title GangVesting
/// @notice Token vesting with a single merkle root
/// @author Rookmate
contract GangVesting is Ownable {
    address public immutable vestingToken;
    address public ecosystemAddress;
    uint32 public constant expiryWindow = 69 days;
    bytes32 public merkleRoot;
    bool public rootLocked;
    uint256 public totalClaimed;

    enum Collection {
        Cat,
        Rat,
        Dog,
        Pigeon,
        BAYC,
        MAYC,
        n1force,
        kanpaiPandas,
        quirkies,
        geezOnApe,
        chimpers,
        chimpersGen,
        reGenerates,
        Grab,
        Team,
        SeedRound,
        CommunityPresale,
        Ecosystem,
        Apechain,
        Liquidity
    }

    struct Vesting {
        uint256 totalClaim;
        uint256 claimed;
        address recipient;
        uint32 start;
        uint32 end;
        uint32 lastClaim;
        Collection collection;
    }

    // Mapping from vesting hash to vesting data
    mapping(bytes32 => Vesting) public vestings;

    error RootIsLocked();
    error InvalidAmount();
    error InvalidAddress();
    error AlreadyClaimed();
    error InvalidMerkleProof();
    error ArrayLengthMustMatch();
    error EcosystemClaimTooEarly();

    event MerkleRootUpdated(bytes32 newRoot);
    event RootLocked();
    event VestingClaimed(bytes32 indexed vestingId, address recipient, uint256 amount);

    constructor(bytes32 _merkleRoot, address _vestingToken) {
        _initializeOwner(msg.sender);
        merkleRoot = _merkleRoot;
        vestingToken = _vestingToken;
    }

    /// @notice Update the merkle root (only if not locked)
    /// @param newRoot The new merkle root
    function updateMerkleRoot(bytes32 newRoot) external onlyOwner {
        if (rootLocked) revert RootIsLocked();
        merkleRoot = newRoot;
        emit MerkleRootUpdated(newRoot);
    }

    /// @notice Lock the merkle root permanently
    function lockRoot() external onlyOwner {
        if (rootLocked) revert RootIsLocked();
        rootLocked = true;
        emit RootLocked();
    }

    /// @notice Set the ecosystem address (only owner)
    /// @param _ecosystemAddress The address of the ecosystem
    function setEcosystemAddress(address _ecosystemAddress) external onlyOwner {
        if (rootLocked) revert RootIsLocked();
        if (_ecosystemAddress == address(0)) revert InvalidAddress();
        ecosystemAddress = _ecosystemAddress;
    }

    /// @notice Claim vested tokens with merkle proof
    /// @param proof Merkle proof to validate the claim
    /// @param collection The collection associated with the vesting
    /// @param recipient The recipient of the vesting
    /// @param totalClaim The total amount being vested
    /// @param start The start time of the vesting
    /// @param end The end time of the vesting
    function claim(
        bytes32[] calldata proof,
        Collection collection,
        address recipient,
        uint256 totalClaim,
        uint32 start,
        uint32 end
    ) external {
        if (totalClaim == 0) revert InvalidAmount();
        if (start >= end) revert InvalidAmount();

        // Generate leaf from vesting data
        bytes32 leaf = keccak256(abi.encodePacked(uint8(collection), recipient, totalClaim, start, end));

        // Verify merkle proof against root
        if (!MerkleProofLib.verifyCalldata(proof, merkleRoot, leaf)) {
            revert InvalidMerkleProof();
        }

        // Get or initialize vesting
        Vesting storage vesting = vestings[leaf];
        if (vesting.totalClaim == 0) {
            // Initialize vesting if first claim
            vesting.totalClaim = totalClaim;
            vesting.collection = collection;
            vesting.recipient = recipient;
            vesting.start = start;
            vesting.end = end;
            vesting.lastClaim = start;
        }

        // Calculate vested amount
        (, uint256 amount) = _calculateVesting(leaf, true);
        if (amount == 0) revert AlreadyClaimed();

        // Update vesting state
        vesting.lastClaim = uint32(block.timestamp);
        unchecked {
            vesting.claimed += amount;
            totalClaimed += amount;
        }

        // Transfer tokens to recipient
        SafeTransferLib.safeTransfer(vestingToken, recipient, amount);

        emit VestingClaimed(leaf, recipient, amount);
    }

    /// @notice Claim unclaimed funds for ecosystem after 69 days from vesting end
    /// @param leaf The vesting identifier
    function claimEcosystemFunds(bytes32 leaf) external {
        Vesting storage vesting = vestings[leaf];

        // Check if 69 days have passed since the end date
        uint256 claimWindow = vesting.end + expiryWindow;
        if (block.timestamp < claimWindow) {
            revert EcosystemClaimTooEarly();
        }

        // Calculate unclaimed amount
        uint256 unclaimedAmount = vesting.totalClaim - vesting.claimed;

        // Ensure there are unclaimed funds
        if (unclaimedAmount == 0) {
            return;
        }

        // Transfer unclaimed funds to the ecosystem address
        SafeTransferLib.safeTransfer(vestingToken, ecosystemAddress, unclaimedAmount);

        // Mark the full amount as claimed
        vesting.claimed = vesting.totalClaim;

        // Emit an event for transparency
        emit VestingClaimed(leaf, ecosystemAddress, unclaimedAmount);
    }

    /// @notice Internal function to get the vested amount
    /// @param leaf The vesting identifier
    /// @return vesting The vesting struct
    /// @return amount The amount vested
    function _calculateVesting(bytes32 leaf, bool isClaim)
        internal
        view
        returns (Vesting storage vesting, uint256 amount)
    {
        vesting = vestings[leaf];

        uint256 start = vesting.start;
        uint256 current = block.timestamp;

        // Early return if vesting hasn't started
        if (current < start) {
            return (vesting, 0);
        }

        // If it hasn't passed a day since last claim
        if (isClaim && current < (vesting.lastClaim + 1 days)) {
            return (vesting, 0);
        }

        uint256 end = vesting.end;
        uint256 total = vesting.totalClaim;
        uint256 claimed = vesting.claimed;

        // If vesting period is complete, return remaining unclaimed amount
        if (current >= end) {
            unchecked {
                if (current > (end + expiryWindow)) {
                    return (vesting, 0);
                }
                // Safe to use unchecked as totalClaim >= claimed is invariant
                return (vesting, total - claimed);
            }
        }

        // Calculate time-based vesting
        unchecked {
            // These operations cannot overflow due to the previous timestamp checks
            uint256 timeSinceStart = current - start;
            uint256 vestingPeriod = end - start;
            uint256 vestedSoFar = (total * timeSinceStart) / vestingPeriod;
            amount = vestedSoFar - claimed;
        }
    }

    /// @notice Get the vesting details for a single vesting
    /// @param collection The collection associated with the vesting
    /// @param recipient The recipient of the vesting
    /// @param totalClaim The total totalClaim being vested
    /// @param start The start time of the vesting
    /// @param end The end time of the vesting
    /// @return The vesting struct and current claimable amount
    function getVesting(Collection collection, address recipient, uint256 totalClaim, uint32 start, uint32 end)
        external
        view
        returns (Vesting memory, uint256 amount)
    {
        bytes32 leaf = keccak256(abi.encodePacked(uint8(collection), recipient, totalClaim, start, end));
        return _calculateVesting(leaf, false);
    }

    /// @notice Get vesting details for multiple vestings at once
    /// @param collections Array of collections
    /// @param recipients Array of recipients
    /// @param totalClaims Array of total claims
    /// @param starts Array of start times
    /// @param ends Array of end times
    /// @return vestingInfo Array of vesting structs
    /// @return amounts Array of claimable amounts
    function getVestingBatch(
        Collection[] calldata collections,
        address[] calldata recipients,
        uint256[] calldata totalClaims,
        uint32[] calldata starts,
        uint32[] calldata ends
    ) external view returns (Vesting[] memory vestingInfo, uint256[] memory amounts) {
        // Check that all arrays have the same length
        uint256 length = collections.length;
        if (
            recipients.length != length || totalClaims.length != length || starts.length != length
                || ends.length != length
        ) revert ArrayLengthMustMatch();

        vestingInfo = new Vesting[](length);
        amounts = new uint256[](length);

        for (uint256 i = 0; i < length; i++) {
            bytes32 leaf =
                keccak256(abi.encodePacked(uint8(collections[i]), recipients[i], totalClaims[i], starts[i], ends[i]));
            (Vesting storage info, uint256 amount) = _calculateVesting(leaf, false);

            // Copy storage struct to memory
            vestingInfo[i] = Vesting({
                totalClaim: info.totalClaim,
                claimed: info.claimed,
                recipient: info.recipient,
                start: info.start,
                end: info.end,
                lastClaim: info.lastClaim,
                collection: info.collection
            });

            amounts[i] = amount;
        }
    }
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

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

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            MERKLE PROOF VERIFICATION OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(proof) {
                // Initialize `offset` to the offset of `proof` elements in memory.
                let offset := add(proof, 0x20)
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(offset, shl(5, mload(proof)))
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, mload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), mload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - The sum of the lengths of `proof` and `leaves` must never overflow.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The memory offset of `proof` must be non-zero
    ///   (i.e. `proof` is not pointing to the scratch space).
    function verifyMultiProof(
        bytes32[] memory proof,
        bytes32 root,
        bytes32[] memory leaves,
        bool[] memory flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // Cache the lengths of the arrays.
            let leavesLength := mload(leaves)
            let proofLength := mload(proof)
            let flagsLength := mload(flags)

            // Advance the pointers of the arrays to point to the data.
            leaves := add(0x20, leaves)
            proof := add(0x20, proof)
            flags := add(0x20, flags)

            // If the number of flags is correct.
            for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flagsLength) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof, shl(5, proofLength))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                leavesLength := shl(5, leavesLength)
                for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                    mstore(add(hashesFront, i), mload(add(leaves, i)))
                }
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, leavesLength)
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flagsLength := add(hashesBack, shl(5, flagsLength))

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(mload(flags)) {
                        // Loads the next proof.
                        b := mload(proof)
                        proof := add(proof, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag.
                    flags := add(flags, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flagsLength)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof)
                    )
                break
            }
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The calldata offset of `proof` must be non-zero
    ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
    function verifyMultiProofCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leaves,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // If the number of flags is correct.
            for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flags.length) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    // forgefmt: disable-next-item
                    isValid := eq(
                        calldataload(
                            xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                        ),
                        root
                    )
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof.offset, shl(5, proof.length))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, shl(5, leaves.length))
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flags.length := add(hashesBack, shl(5, flags.length))

                // We don't need to make a copy of `proof.offset` or `flags.offset`,
                // as they are pass-by-value (this trick may not always save gas).

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(calldataload(flags.offset)) {
                        // Loads the next proof.
                        b := calldataload(proof.offset)
                        proof.offset := add(proof.offset, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag offset.
                    flags.offset := add(flags.offset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flags.length)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof.offset)
                    )
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes32 array.
    function emptyProof() internal pure returns (bytes32[] calldata proof) {
        /// @solidity memory-safe-assembly
        assembly {
            proof.length := 0
        }
    }

    /// @dev Returns an empty calldata bytes32 array.
    function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
        /// @solidity memory-safe-assembly
        assembly {
            leaves.length := 0
        }
    }

    /// @dev Returns an empty calldata bool array.
    function emptyFlags() internal pure returns (bool[] calldata flags) {
        /// @solidity memory-safe-assembly
        assembly {
            flags.length := 0
        }
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@solady/=lib/solady/",
    "@murky/=lib/murky/src/",
    "@openzeppelin/contracts/=lib/murky/lib/openzeppelin-contracts/contracts/",
    "erc4626-tests/=lib/murky/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "murky/=lib/murky/",
    "openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"address","name":"_vestingToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ArrayLengthMustMatch","type":"error"},{"inputs":[],"name":"EcosystemClaimTooEarly","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidMerkleProof","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"RootIsLocked","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newRoot","type":"bytes32"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"RootLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestingClaimed","type":"event"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"enum GangVesting.Collection","name":"collection","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"totalClaim","type":"uint256"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"leaf","type":"bytes32"}],"name":"claimEcosystemFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"ecosystemAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expiryWindow","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum GangVesting.Collection","name":"collection","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"totalClaim","type":"uint256"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"}],"name":"getVesting","outputs":[{"components":[{"internalType":"uint256","name":"totalClaim","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"lastClaim","type":"uint32"},{"internalType":"enum GangVesting.Collection","name":"collection","type":"uint8"}],"internalType":"struct GangVesting.Vesting","name":"","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum GangVesting.Collection[]","name":"collections","type":"uint8[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"totalClaims","type":"uint256[]"},{"internalType":"uint32[]","name":"starts","type":"uint32[]"},{"internalType":"uint32[]","name":"ends","type":"uint32[]"}],"name":"getVestingBatch","outputs":[{"components":[{"internalType":"uint256","name":"totalClaim","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"lastClaim","type":"uint32"},{"internalType":"enum GangVesting.Collection","name":"collection","type":"uint8"}],"internalType":"struct GangVesting.Vesting[]","name":"vestingInfo","type":"tuple[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"rootLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ecosystemAddress","type":"address"}],"name":"setEcosystemAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newRoot","type":"bytes32"}],"name":"updateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"vestings","outputs":[{"internalType":"uint256","name":"totalClaim","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"uint32","name":"lastClaim","type":"uint32"},{"internalType":"enum GangVesting.Collection","name":"collection","type":"uint8"}],"stateMutability":"view","type":"function"}]

60a060405234801562000010575f80fd5b506040516200253e3803806200253e83398181016040528101906200003691906200020c565b62000047336200008a60201b60201c565b816001819055508073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050505062000251565b6200009a6200016b60201b60201c565b1562000115577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805415620000d657630dc149f05f526004601cfd5b8160601b60601c9150811560ff1b82178155815f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a35062000168565b8060601b60601c9050807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a35b50565b5f90565b5f80fd5b5f819050919050565b620001878162000173565b811462000192575f80fd5b50565b5f81519050620001a5816200017c565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620001d682620001ab565b9050919050565b620001e881620001ca565b8114620001f3575f80fd5b50565b5f815190506200020681620001dd565b92915050565b5f80604083850312156200022557620002246200016f565b5b5f620002348582860162000195565b92505060206200024785828601620001f6565b9150509250929050565b6080516122c6620002785f395f818161047c01528181610c41015261101e01526122c65ff3fe608060405260043610610129575f3560e01c8063715018a6116100aa578063a44f6a861161006e578063a44f6a8614610369578063a8e01b2814610391578063d54ad2a1146103b9578063f04e283e146103e3578063f2fde38b146103ff578063fee81cf41461041b57610129565b8063715018a6146102a157806384838636146102ab5780638da5cb5b146102d55780638edca5c6146102ff5780639fb0c41d1461032757610129565b80632eb4a7ab116100f15780632eb4a7ab146101cb5780633837973f146101f55780633b982284146102325780634783f0ef1461026f57806354d1f13d1461029757610129565b806306a5cb3a1461012d57806319d152fa1461015757806321b607e41461018157806324456781146101ab57806325692962146101c1575b5f80fd5b348015610138575f80fd5b50610141610457565b60405161014e919061159b565b60405180910390f35b348015610162575f80fd5b5061016b61047a565b604051610178919061159b565b60405180910390f35b34801561018c575f80fd5b5061019561049e565b6040516101a291906115ce565b60405180910390f35b3480156101b6575f80fd5b506101bf6104b0565b005b6101c9610546565b005b3480156101d6575f80fd5b506101df610597565b6040516101ec91906115ff565b60405180910390f35b348015610200575f80fd5b5061021b600480360381019061021691906116d9565b61059d565b60405161022992919061188b565b60405180910390f35b34801561023d575f80fd5b5061025860048036038101906102539190611a13565b610725565b604051610266929190611d06565b60405180910390f35b34801561027a575f80fd5b5061029560048036038101906102909190611d65565b610a93565b005b61029f610b22565b005b6102a9610b5b565b005b3480156102b6575f80fd5b506102bf610b6e565b6040516102cc9190611d9f565b60405180910390f35b3480156102e0575f80fd5b506102e9610b75565b6040516102f6919061159b565b60405180910390f35b34801561030a575f80fd5b5061032560048036038101906103209190611d65565b610b9d565b005b348015610332575f80fd5b5061034d60048036038101906103489190611d65565b610cf2565b6040516103609796959493929190611dc7565b60405180910390f35b348015610374575f80fd5b5061038f600480360381019061038a9190611e89565b610d8b565b005b34801561039c575f80fd5b506103b760048036038101906103b29190611f33565b61108a565b005b3480156103c4575f80fd5b506103cd61117f565b6040516103da9190611f5e565b60405180910390f35b6103fd60048036038101906103f89190611f33565b611185565b005b61041960048036038101906104149190611f33565b6111c3565b005b348015610426575f80fd5b50610441600480360381019061043c9190611f33565b6111ec565b60405161044e9190611f5e565b60405180910390f35b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60025f9054906101000a900460ff1681565b6104b8611205565b60025f9054906101000a900460ff16156104fe576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160025f6101000a81548160ff0219169083151502179055507f4d59dcc3a0812525cd8c41f1023bc183efcbd84e7d9e1c352376126e6ef489fc60405160405180910390a1565b5f61054f61123c565b67ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b60015481565b6105a56114ec565b5f808760138111156105ba576105b961177d565b5b878787876040516020016105d2959493929190612050565b6040516020818303038152906040528051906020012090506105f4815f611246565b816040518060e00160405290815f820154815260200160018201548152602001600282015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016002820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160028201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600382015f9054906101000a900460ff1660138111156106fe576106fd61177d565b5b60138111156107105761070f61177d565b5b81525050915092509250509550959350505050565b6060805f8c8c90509050808b8b90501415806107445750808989905014155b806107525750808787905014155b806107605750808585905014155b15610797576040517fd7385e6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156107b1576107b06120ae565b5b6040519080825280602002602001820160405280156107ea57816020015b6107d76114ec565b8152602001906001900390816107cf5790505b5092508067ffffffffffffffff811115610807576108066120ae565b5b6040519080825280602002602001820160405280156108355781602001602082028036833780820191505090505b5091505f5b81811015610a82575f8e8e83818110610856576108556120db565b5b905060200201602081019061086b9190612108565b601381111561087d5761087c61177d565b5b8d8d848181106108905761088f6120db565b5b90506020020160208101906108a59190611f33565b8c8c858181106108b8576108b76120db565b5b905060200201358b8b868181106108d2576108d16120db565b5b90506020020160208101906108e79190612133565b8a8a878181106108fa576108f96120db565b5b905060200201602081019061090f9190612133565b604051602001610923959493929190612050565b6040516020818303038152906040528051906020012090505f80610947835f611246565b915091506040518060e00160405280835f0154815260200183600101548152602001836002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018360020160149054906101000a900463ffffffff1663ffffffff1681526020018360020160189054906101000a900463ffffffff1663ffffffff16815260200183600201601c9054906101000a900463ffffffff1663ffffffff168152602001836003015f9054906101000a900460ff166013811115610a2b57610a2a61177d565b5b815250878581518110610a4157610a406120db565b5b602002602001018190525080868581518110610a6057610a5f6120db565b5b6020026020010181815250505050508080610a7a9061218b565b91505061083a565b50509a509a98505050505050505050565b610a9b611205565b60025f9054906101000a900460ff1615610ae1576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea94181604051610b1791906115ff565b60405180910390a150565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b610b63611205565b610b6c5f611378565b565b625af78081565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754905090565b5f60045f8381526020019081526020015f2090505f625af7808260020160189054906101000a900463ffffffff16610bd591906121d2565b63ffffffff16905080421015610c17576040517f0808637d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8260010154835f0154610c2b9190612209565b90505f8103610c3c57505050610cef565b610c867f00000000000000000000000000000000000000000000000000000000000000005f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff168361143e565b825f01548360010181905550837ff1a0befba89b9e81420d704152e9e164ae60fb2170011c111da52b9917d616795f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683604051610ce392919061223c565b60405180910390a25050505b50565b6004602052805f5260405f205f91509050805f015490806001015490806002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020160149054906101000a900463ffffffff16908060020160189054906101000a900463ffffffff169080600201601c9054906101000a900463ffffffff1690806003015f9054906101000a900460ff16905087565b5f8303610dc4576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8063ffffffff168263ffffffff1610610e09576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f856013811115610e1d57610e1c61177d565b5b85858585604051602001610e35959493929190612050565b604051602081830303815290604052805190602001209050610e5b888860015484611492565b610e91576040517fb05e92fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60045f8381526020019081526020015f2090505f815f015403610f8e5784815f018190555086816003015f6101000a81548160ff02191690836013811115610edd57610edc61177d565b5b021790555085816002015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838160020160146101000a81548163ffffffff021916908363ffffffff160217905550828160020160186101000a81548163ffffffff021916908363ffffffff1602179055508381600201601c6101000a81548163ffffffff021916908363ffffffff1602179055505b5f610f9a836001611246565b9150505f8103610fd6576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4282600201601c6101000a81548163ffffffff021916908363ffffffff16021790555080826001015f82825401925050819055508060035f82825401925050819055506110447f0000000000000000000000000000000000000000000000000000000000000000888361143e565b827ff1a0befba89b9e81420d704152e9e164ae60fb2170011c111da52b9917d61679888360405161107692919061223c565b60405180910390a250505050505050505050565b611092611205565b60025f9054906101000a900460ff16156110d8576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361113d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60035481565b61118d611205565b63389a75e1600c52805f526020600c2080544211156111b357636f5e88185f526004601cfd5b5f8155506111c081611378565b50565b6111cb611205565b8060601b6111e057637448fbae5f526004601cfd5b6111e981611378565b50565b5f63389a75e1600c52815f526020600c20549050919050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754331461123a576382b429005f526004601cfd5b565b5f6202a300905090565b5f8060045f8581526020019081526020015f2091505f8260020160149054906101000a900463ffffffff1663ffffffff1690505f4290508181101561129257835f935093505050611371565b8480156112c657506201518084600201601c9054906101000a900463ffffffff166112bd91906121d2565b63ffffffff1681105b156112d857835f935093505050611371565b5f8460020160189054906101000a900463ffffffff1663ffffffff1690505f855f015490505f8660010154905082841061134157625af78063ffffffff16830184111561132f57865f965096505050505050611371565b86818303965096505050505050611371565b5f85850390505f86850390505f81838602816113605761135f612263565b5b049050838103985050505050505050505b9250929050565b6113806114e8565b156113e5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b821781555061143b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3818155505b50565b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af18060015f51141661148857803d853b151710611487576390b8ec185f526004601cfd5b5b5f60345250505050565b5f83156114db578360051b8501855b6001156114d8578035841160051b8481528135602082185260405f2094506020820191508282106114d257506114d8565b506114a1565b50505b8282149050949350505050565b5f90565b6040518060e001604052805f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f63ffffffff1681526020015f63ffffffff1681526020015f63ffffffff1681526020015f60138111156115565761155561177d565b5b81525090565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6115858261155c565b9050919050565b6115958161157b565b82525050565b5f6020820190506115ae5f83018461158c565b92915050565b5f8115159050919050565b6115c8816115b4565b82525050565b5f6020820190506115e15f8301846115bf565b92915050565b5f819050919050565b6115f9816115e7565b82525050565b5f6020820190506116125f8301846115f0565b92915050565b5f80fd5b5f80fd5b6014811061162c575f80fd5b50565b5f8135905061163d81611620565b92915050565b61164c8161157b565b8114611656575f80fd5b50565b5f8135905061166781611643565b92915050565b5f819050919050565b61167f8161166d565b8114611689575f80fd5b50565b5f8135905061169a81611676565b92915050565b5f63ffffffff82169050919050565b6116b8816116a0565b81146116c2575f80fd5b50565b5f813590506116d3816116af565b92915050565b5f805f805f60a086880312156116f2576116f1611618565b5b5f6116ff8882890161162f565b955050602061171088828901611659565b94505060406117218882890161168c565b9350506060611732888289016116c5565b9250506080611743888289016116c5565b9150509295509295909350565b6117598161166d565b82525050565b6117688161157b565b82525050565b611777816116a0565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b601481106117bb576117ba61177d565b5b50565b5f8190506117cb826117aa565b919050565b5f6117da826117be565b9050919050565b6117ea816117d0565b82525050565b60e082015f8201516118045f850182611750565b5060208201516118176020850182611750565b50604082015161182a604085018261175f565b50606082015161183d606085018261176e565b506080820151611850608085018261176e565b5060a082015161186360a085018261176e565b5060c082015161187660c08501826117e1565b50505050565b6118858161166d565b82525050565b5f6101008201905061189f5f8301856117f0565b6118ac60e083018461187c565b9392505050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f8401126118d4576118d36118b3565b5b8235905067ffffffffffffffff8111156118f1576118f06118b7565b5b60208301915083602082028301111561190d5761190c6118bb565b5b9250929050565b5f8083601f840112611929576119286118b3565b5b8235905067ffffffffffffffff811115611946576119456118b7565b5b602083019150836020820283011115611962576119616118bb565b5b9250929050565b5f8083601f84011261197e5761197d6118b3565b5b8235905067ffffffffffffffff81111561199b5761199a6118b7565b5b6020830191508360208202830111156119b7576119b66118bb565b5b9250929050565b5f8083601f8401126119d3576119d26118b3565b5b8235905067ffffffffffffffff8111156119f0576119ef6118b7565b5b602083019150836020820283011115611a0c57611a0b6118bb565b5b9250929050565b5f805f805f805f805f8060a08b8d031215611a3157611a30611618565b5b5f8b013567ffffffffffffffff811115611a4e57611a4d61161c565b5b611a5a8d828e016118bf565b9a509a505060208b013567ffffffffffffffff811115611a7d57611a7c61161c565b5b611a898d828e01611914565b985098505060408b013567ffffffffffffffff811115611aac57611aab61161c565b5b611ab88d828e01611969565b965096505060608b013567ffffffffffffffff811115611adb57611ada61161c565b5b611ae78d828e016119be565b945094505060808b013567ffffffffffffffff811115611b0a57611b0961161c565b5b611b168d828e016119be565b92509250509295989b9194979a5092959850565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b60e082015f820151611b675f850182611750565b506020820151611b7a6020850182611750565b506040820151611b8d604085018261175f565b506060820151611ba0606085018261176e565b506080820151611bb3608085018261176e565b5060a0820151611bc660a085018261176e565b5060c0820151611bd960c08501826117e1565b50505050565b5f611bea8383611b53565b60e08301905092915050565b5f602082019050919050565b5f611c0c82611b2a565b611c168185611b34565b9350611c2183611b44565b805f5b83811015611c51578151611c388882611bdf565b9750611c4383611bf6565b925050600181019050611c24565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f611c928383611750565b60208301905092915050565b5f602082019050919050565b5f611cb482611c5e565b611cbe8185611c68565b9350611cc983611c78565b805f5b83811015611cf9578151611ce08882611c87565b9750611ceb83611c9e565b925050600181019050611ccc565b5085935050505092915050565b5f6040820190508181035f830152611d1e8185611c02565b90508181036020830152611d328184611caa565b90509392505050565b611d44816115e7565b8114611d4e575f80fd5b50565b5f81359050611d5f81611d3b565b92915050565b5f60208284031215611d7a57611d79611618565b5b5f611d8784828501611d51565b91505092915050565b611d99816116a0565b82525050565b5f602082019050611db25f830184611d90565b92915050565b611dc1816117d0565b82525050565b5f60e082019050611dda5f83018a61187c565b611de7602083018961187c565b611df4604083018861158c565b611e016060830187611d90565b611e0e6080830186611d90565b611e1b60a0830185611d90565b611e2860c0830184611db8565b98975050505050505050565b5f8083601f840112611e4957611e486118b3565b5b8235905067ffffffffffffffff811115611e6657611e656118b7565b5b602083019150836020820283011115611e8257611e816118bb565b5b9250929050565b5f805f805f805f60c0888a031215611ea457611ea3611618565b5b5f88013567ffffffffffffffff811115611ec157611ec061161c565b5b611ecd8a828b01611e34565b97509750506020611ee08a828b0161162f565b9550506040611ef18a828b01611659565b9450506060611f028a828b0161168c565b9350506080611f138a828b016116c5565b92505060a0611f248a828b016116c5565b91505092959891949750929550565b5f60208284031215611f4857611f47611618565b5b5f611f5584828501611659565b91505092915050565b5f602082019050611f715f83018461187c565b92915050565b5f60ff82169050919050565b5f8160f81b9050919050565b5f611f9982611f83565b9050919050565b611fb1611fac82611f77565b611f8f565b82525050565b5f8160601b9050919050565b5f611fcd82611fb7565b9050919050565b5f611fde82611fc3565b9050919050565b611ff6611ff18261157b565b611fd4565b82525050565b5f819050919050565b6120166120118261166d565b611ffc565b82525050565b5f8160e01b9050919050565b5f6120328261201c565b9050919050565b61204a612045826116a0565b612028565b82525050565b5f61205b8288611fa0565b60018201915061206b8287611fe5565b60148201915061207b8286612005565b60208201915061208b8285612039565b60048201915061209b8284612039565b6004820191508190509695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6020828403121561211d5761211c611618565b5b5f61212a8482850161162f565b91505092915050565b5f6020828403121561214857612147611618565b5b5f612155848285016116c5565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6121958261166d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036121c7576121c661215e565b5b600182019050919050565b5f6121dc826116a0565b91506121e7836116a0565b9250828201905063ffffffff8111156122035761220261215e565b5b92915050565b5f6122138261166d565b915061221e8361166d565b92508282039050818111156122365761223561215e565b5b92915050565b5f60408201905061224f5f83018561158c565b61225c602083018461187c565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffdfea26469706673582212202f8004ba7bf64a20db7444d7068ee2c08453641a30f82fe5ed9a6c192b5f9dd664736f6c6343000815003369a5080be5836b60bd3c9129814e9bdb67db2ec8986d146a9d9efb24506c222c00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b7

Deployed Bytecode

0x608060405260043610610129575f3560e01c8063715018a6116100aa578063a44f6a861161006e578063a44f6a8614610369578063a8e01b2814610391578063d54ad2a1146103b9578063f04e283e146103e3578063f2fde38b146103ff578063fee81cf41461041b57610129565b8063715018a6146102a157806384838636146102ab5780638da5cb5b146102d55780638edca5c6146102ff5780639fb0c41d1461032757610129565b80632eb4a7ab116100f15780632eb4a7ab146101cb5780633837973f146101f55780633b982284146102325780634783f0ef1461026f57806354d1f13d1461029757610129565b806306a5cb3a1461012d57806319d152fa1461015757806321b607e41461018157806324456781146101ab57806325692962146101c1575b5f80fd5b348015610138575f80fd5b50610141610457565b60405161014e919061159b565b60405180910390f35b348015610162575f80fd5b5061016b61047a565b604051610178919061159b565b60405180910390f35b34801561018c575f80fd5b5061019561049e565b6040516101a291906115ce565b60405180910390f35b3480156101b6575f80fd5b506101bf6104b0565b005b6101c9610546565b005b3480156101d6575f80fd5b506101df610597565b6040516101ec91906115ff565b60405180910390f35b348015610200575f80fd5b5061021b600480360381019061021691906116d9565b61059d565b60405161022992919061188b565b60405180910390f35b34801561023d575f80fd5b5061025860048036038101906102539190611a13565b610725565b604051610266929190611d06565b60405180910390f35b34801561027a575f80fd5b5061029560048036038101906102909190611d65565b610a93565b005b61029f610b22565b005b6102a9610b5b565b005b3480156102b6575f80fd5b506102bf610b6e565b6040516102cc9190611d9f565b60405180910390f35b3480156102e0575f80fd5b506102e9610b75565b6040516102f6919061159b565b60405180910390f35b34801561030a575f80fd5b5061032560048036038101906103209190611d65565b610b9d565b005b348015610332575f80fd5b5061034d60048036038101906103489190611d65565b610cf2565b6040516103609796959493929190611dc7565b60405180910390f35b348015610374575f80fd5b5061038f600480360381019061038a9190611e89565b610d8b565b005b34801561039c575f80fd5b506103b760048036038101906103b29190611f33565b61108a565b005b3480156103c4575f80fd5b506103cd61117f565b6040516103da9190611f5e565b60405180910390f35b6103fd60048036038101906103f89190611f33565b611185565b005b61041960048036038101906104149190611f33565b6111c3565b005b348015610426575f80fd5b50610441600480360381019061043c9190611f33565b6111ec565b60405161044e9190611f5e565b60405180910390f35b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b781565b60025f9054906101000a900460ff1681565b6104b8611205565b60025f9054906101000a900460ff16156104fe576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160025f6101000a81548160ff0219169083151502179055507f4d59dcc3a0812525cd8c41f1023bc183efcbd84e7d9e1c352376126e6ef489fc60405160405180910390a1565b5f61054f61123c565b67ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b60015481565b6105a56114ec565b5f808760138111156105ba576105b961177d565b5b878787876040516020016105d2959493929190612050565b6040516020818303038152906040528051906020012090506105f4815f611246565b816040518060e00160405290815f820154815260200160018201548152602001600282015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016002820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160028201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600382015f9054906101000a900460ff1660138111156106fe576106fd61177d565b5b60138111156107105761070f61177d565b5b81525050915092509250509550959350505050565b6060805f8c8c90509050808b8b90501415806107445750808989905014155b806107525750808787905014155b806107605750808585905014155b15610797576040517fd7385e6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156107b1576107b06120ae565b5b6040519080825280602002602001820160405280156107ea57816020015b6107d76114ec565b8152602001906001900390816107cf5790505b5092508067ffffffffffffffff811115610807576108066120ae565b5b6040519080825280602002602001820160405280156108355781602001602082028036833780820191505090505b5091505f5b81811015610a82575f8e8e83818110610856576108556120db565b5b905060200201602081019061086b9190612108565b601381111561087d5761087c61177d565b5b8d8d848181106108905761088f6120db565b5b90506020020160208101906108a59190611f33565b8c8c858181106108b8576108b76120db565b5b905060200201358b8b868181106108d2576108d16120db565b5b90506020020160208101906108e79190612133565b8a8a878181106108fa576108f96120db565b5b905060200201602081019061090f9190612133565b604051602001610923959493929190612050565b6040516020818303038152906040528051906020012090505f80610947835f611246565b915091506040518060e00160405280835f0154815260200183600101548152602001836002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018360020160149054906101000a900463ffffffff1663ffffffff1681526020018360020160189054906101000a900463ffffffff1663ffffffff16815260200183600201601c9054906101000a900463ffffffff1663ffffffff168152602001836003015f9054906101000a900460ff166013811115610a2b57610a2a61177d565b5b815250878581518110610a4157610a406120db565b5b602002602001018190525080868581518110610a6057610a5f6120db565b5b6020026020010181815250505050508080610a7a9061218b565b91505061083a565b50509a509a98505050505050505050565b610a9b611205565b60025f9054906101000a900460ff1615610ae1576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea94181604051610b1791906115ff565b60405180910390a150565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b610b63611205565b610b6c5f611378565b565b625af78081565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754905090565b5f60045f8381526020019081526020015f2090505f625af7808260020160189054906101000a900463ffffffff16610bd591906121d2565b63ffffffff16905080421015610c17576040517f0808637d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8260010154835f0154610c2b9190612209565b90505f8103610c3c57505050610cef565b610c867f00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b75f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff168361143e565b825f01548360010181905550837ff1a0befba89b9e81420d704152e9e164ae60fb2170011c111da52b9917d616795f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683604051610ce392919061223c565b60405180910390a25050505b50565b6004602052805f5260405f205f91509050805f015490806001015490806002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020160149054906101000a900463ffffffff16908060020160189054906101000a900463ffffffff169080600201601c9054906101000a900463ffffffff1690806003015f9054906101000a900460ff16905087565b5f8303610dc4576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8063ffffffff168263ffffffff1610610e09576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f856013811115610e1d57610e1c61177d565b5b85858585604051602001610e35959493929190612050565b604051602081830303815290604052805190602001209050610e5b888860015484611492565b610e91576040517fb05e92fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60045f8381526020019081526020015f2090505f815f015403610f8e5784815f018190555086816003015f6101000a81548160ff02191690836013811115610edd57610edc61177d565b5b021790555085816002015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838160020160146101000a81548163ffffffff021916908363ffffffff160217905550828160020160186101000a81548163ffffffff021916908363ffffffff1602179055508381600201601c6101000a81548163ffffffff021916908363ffffffff1602179055505b5f610f9a836001611246565b9150505f8103610fd6576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b4282600201601c6101000a81548163ffffffff021916908363ffffffff16021790555080826001015f82825401925050819055508060035f82825401925050819055506110447f00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b7888361143e565b827ff1a0befba89b9e81420d704152e9e164ae60fb2170011c111da52b9917d61679888360405161107692919061223c565b60405180910390a250505050505050505050565b611092611205565b60025f9054906101000a900460ff16156110d8576040517f1ef0529200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361113d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60035481565b61118d611205565b63389a75e1600c52805f526020600c2080544211156111b357636f5e88185f526004601cfd5b5f8155506111c081611378565b50565b6111cb611205565b8060601b6111e057637448fbae5f526004601cfd5b6111e981611378565b50565b5f63389a75e1600c52815f526020600c20549050919050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754331461123a576382b429005f526004601cfd5b565b5f6202a300905090565b5f8060045f8581526020019081526020015f2091505f8260020160149054906101000a900463ffffffff1663ffffffff1690505f4290508181101561129257835f935093505050611371565b8480156112c657506201518084600201601c9054906101000a900463ffffffff166112bd91906121d2565b63ffffffff1681105b156112d857835f935093505050611371565b5f8460020160189054906101000a900463ffffffff1663ffffffff1690505f855f015490505f8660010154905082841061134157625af78063ffffffff16830184111561132f57865f965096505050505050611371565b86818303965096505050505050611371565b5f85850390505f86850390505f81838602816113605761135f612263565b5b049050838103985050505050505050505b9250929050565b6113806114e8565b156113e5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b821781555061143b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3818155505b50565b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af18060015f51141661148857803d853b151710611487576390b8ec185f526004601cfd5b5b5f60345250505050565b5f83156114db578360051b8501855b6001156114d8578035841160051b8481528135602082185260405f2094506020820191508282106114d257506114d8565b506114a1565b50505b8282149050949350505050565b5f90565b6040518060e001604052805f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f63ffffffff1681526020015f63ffffffff1681526020015f63ffffffff1681526020015f60138111156115565761155561177d565b5b81525090565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6115858261155c565b9050919050565b6115958161157b565b82525050565b5f6020820190506115ae5f83018461158c565b92915050565b5f8115159050919050565b6115c8816115b4565b82525050565b5f6020820190506115e15f8301846115bf565b92915050565b5f819050919050565b6115f9816115e7565b82525050565b5f6020820190506116125f8301846115f0565b92915050565b5f80fd5b5f80fd5b6014811061162c575f80fd5b50565b5f8135905061163d81611620565b92915050565b61164c8161157b565b8114611656575f80fd5b50565b5f8135905061166781611643565b92915050565b5f819050919050565b61167f8161166d565b8114611689575f80fd5b50565b5f8135905061169a81611676565b92915050565b5f63ffffffff82169050919050565b6116b8816116a0565b81146116c2575f80fd5b50565b5f813590506116d3816116af565b92915050565b5f805f805f60a086880312156116f2576116f1611618565b5b5f6116ff8882890161162f565b955050602061171088828901611659565b94505060406117218882890161168c565b9350506060611732888289016116c5565b9250506080611743888289016116c5565b9150509295509295909350565b6117598161166d565b82525050565b6117688161157b565b82525050565b611777816116a0565b82525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b601481106117bb576117ba61177d565b5b50565b5f8190506117cb826117aa565b919050565b5f6117da826117be565b9050919050565b6117ea816117d0565b82525050565b60e082015f8201516118045f850182611750565b5060208201516118176020850182611750565b50604082015161182a604085018261175f565b50606082015161183d606085018261176e565b506080820151611850608085018261176e565b5060a082015161186360a085018261176e565b5060c082015161187660c08501826117e1565b50505050565b6118858161166d565b82525050565b5f6101008201905061189f5f8301856117f0565b6118ac60e083018461187c565b9392505050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f8401126118d4576118d36118b3565b5b8235905067ffffffffffffffff8111156118f1576118f06118b7565b5b60208301915083602082028301111561190d5761190c6118bb565b5b9250929050565b5f8083601f840112611929576119286118b3565b5b8235905067ffffffffffffffff811115611946576119456118b7565b5b602083019150836020820283011115611962576119616118bb565b5b9250929050565b5f8083601f84011261197e5761197d6118b3565b5b8235905067ffffffffffffffff81111561199b5761199a6118b7565b5b6020830191508360208202830111156119b7576119b66118bb565b5b9250929050565b5f8083601f8401126119d3576119d26118b3565b5b8235905067ffffffffffffffff8111156119f0576119ef6118b7565b5b602083019150836020820283011115611a0c57611a0b6118bb565b5b9250929050565b5f805f805f805f805f8060a08b8d031215611a3157611a30611618565b5b5f8b013567ffffffffffffffff811115611a4e57611a4d61161c565b5b611a5a8d828e016118bf565b9a509a505060208b013567ffffffffffffffff811115611a7d57611a7c61161c565b5b611a898d828e01611914565b985098505060408b013567ffffffffffffffff811115611aac57611aab61161c565b5b611ab88d828e01611969565b965096505060608b013567ffffffffffffffff811115611adb57611ada61161c565b5b611ae78d828e016119be565b945094505060808b013567ffffffffffffffff811115611b0a57611b0961161c565b5b611b168d828e016119be565b92509250509295989b9194979a5092959850565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b60e082015f820151611b675f850182611750565b506020820151611b7a6020850182611750565b506040820151611b8d604085018261175f565b506060820151611ba0606085018261176e565b506080820151611bb3608085018261176e565b5060a0820151611bc660a085018261176e565b5060c0820151611bd960c08501826117e1565b50505050565b5f611bea8383611b53565b60e08301905092915050565b5f602082019050919050565b5f611c0c82611b2a565b611c168185611b34565b9350611c2183611b44565b805f5b83811015611c51578151611c388882611bdf565b9750611c4383611bf6565b925050600181019050611c24565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f611c928383611750565b60208301905092915050565b5f602082019050919050565b5f611cb482611c5e565b611cbe8185611c68565b9350611cc983611c78565b805f5b83811015611cf9578151611ce08882611c87565b9750611ceb83611c9e565b925050600181019050611ccc565b5085935050505092915050565b5f6040820190508181035f830152611d1e8185611c02565b90508181036020830152611d328184611caa565b90509392505050565b611d44816115e7565b8114611d4e575f80fd5b50565b5f81359050611d5f81611d3b565b92915050565b5f60208284031215611d7a57611d79611618565b5b5f611d8784828501611d51565b91505092915050565b611d99816116a0565b82525050565b5f602082019050611db25f830184611d90565b92915050565b611dc1816117d0565b82525050565b5f60e082019050611dda5f83018a61187c565b611de7602083018961187c565b611df4604083018861158c565b611e016060830187611d90565b611e0e6080830186611d90565b611e1b60a0830185611d90565b611e2860c0830184611db8565b98975050505050505050565b5f8083601f840112611e4957611e486118b3565b5b8235905067ffffffffffffffff811115611e6657611e656118b7565b5b602083019150836020820283011115611e8257611e816118bb565b5b9250929050565b5f805f805f805f60c0888a031215611ea457611ea3611618565b5b5f88013567ffffffffffffffff811115611ec157611ec061161c565b5b611ecd8a828b01611e34565b97509750506020611ee08a828b0161162f565b9550506040611ef18a828b01611659565b9450506060611f028a828b0161168c565b9350506080611f138a828b016116c5565b92505060a0611f248a828b016116c5565b91505092959891949750929550565b5f60208284031215611f4857611f47611618565b5b5f611f5584828501611659565b91505092915050565b5f602082019050611f715f83018461187c565b92915050565b5f60ff82169050919050565b5f8160f81b9050919050565b5f611f9982611f83565b9050919050565b611fb1611fac82611f77565b611f8f565b82525050565b5f8160601b9050919050565b5f611fcd82611fb7565b9050919050565b5f611fde82611fc3565b9050919050565b611ff6611ff18261157b565b611fd4565b82525050565b5f819050919050565b6120166120118261166d565b611ffc565b82525050565b5f8160e01b9050919050565b5f6120328261201c565b9050919050565b61204a612045826116a0565b612028565b82525050565b5f61205b8288611fa0565b60018201915061206b8287611fe5565b60148201915061207b8286612005565b60208201915061208b8285612039565b60048201915061209b8284612039565b6004820191508190509695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6020828403121561211d5761211c611618565b5b5f61212a8482850161162f565b91505092915050565b5f6020828403121561214857612147611618565b5b5f612155848285016116c5565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6121958261166d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036121c7576121c661215e565b5b600182019050919050565b5f6121dc826116a0565b91506121e7836116a0565b9250828201905063ffffffff8111156122035761220261215e565b5b92915050565b5f6122138261166d565b915061221e8361166d565b92508282039050818111156122365761223561215e565b5b92915050565b5f60408201905061224f5f83018561158c565b61225c602083018461187c565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffdfea26469706673582212202f8004ba7bf64a20db7444d7068ee2c08453641a30f82fe5ed9a6c192b5f9dd664736f6c63430008150033

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

69a5080be5836b60bd3c9129814e9bdb67db2ec8986d146a9d9efb24506c222c00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b7

-----Decoded View---------------
Arg [0] : _merkleRoot (bytes32): 0x69a5080be5836b60bd3c9129814e9bdb67db2ec8986d146a9d9efb24506c222c
Arg [1] : _vestingToken (address): 0x28D9428716c2F6E566766dc8f5f56C1aF43042b7

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 69a5080be5836b60bd3c9129814e9bdb67db2ec8986d146a9d9efb24506c222c
Arg [1] : 00000000000000000000000028d9428716c2f6e566766dc8f5f56c1af43042b7


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0xf5918F8cF2E6D9DfD78EF15a7cACfBa97a7e1e1b
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.