ETH Price: $1,962.91 (+0.41%)
Gas: 0.04 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim Vesting245032222026-02-21 5:43:352 mins ago1771652615IN
0x5c3Acd26...2Ce31d606
0 ETH0.000009940.08203995
Withdraw Claimed245032052026-02-21 5:39:595 mins ago1771652399IN
0x5c3Acd26...2Ce31d606
0 ETH0.000006950.0860645
Claim Vesting245030952026-02-21 5:17:5927 mins ago1771651079IN
0x5c3Acd26...2Ce31d606
0 ETH0.000391172.02994474
Claim Vesting245030882026-02-21 5:16:3529 mins ago1771650995IN
0x5c3Acd26...2Ce31d606
0 ETH0.000391392.03172359
Claim Vesting245030852026-02-21 5:15:5929 mins ago1771650959IN
0x5c3Acd26...2Ce31d606
0 ETH0.000391352.0314585
Claim Vesting245030762026-02-21 5:14:1131 mins ago1771650851IN
0x5c3Acd26...2Ce31d606
0 ETH0.000391082.02992155
Claim Vesting245029232026-02-21 4:43:351 hr ago1771649015IN
0x5c3Acd26...2Ce31d606
0 ETH0.00000430.03548698
Withdraw Claimed245029202026-02-21 4:42:591 hr ago1771648979IN
0x5c3Acd26...2Ce31d606
0 ETH0.000003160.03921507
Claim Vesting245028532026-02-21 4:29:111 hr ago1771648151IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004350.03614389
Claim Vesting245028452026-02-21 4:27:351 hr ago1771648055IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004330.03575756
Claim Vesting245028422026-02-21 4:26:591 hr ago1771648019IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004190.03479823
Claim Vesting245028392026-02-21 4:26:231 hr ago1771647983IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004150.03428756
Claim Vesting245028372026-02-21 4:25:591 hr ago1771647959IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004330.03571317
Claim Vesting245028332026-02-21 4:25:111 hr ago1771647911IN
0x5c3Acd26...2Ce31d606
0 ETH0.00000420.0349185
Claim Vesting245028332026-02-21 4:25:111 hr ago1771647911IN
0x5c3Acd26...2Ce31d606
0 ETH0.00000430.0355075
Withdraw Claimed245027232026-02-21 4:03:111 hr ago1771646591IN
0x5c3Acd26...2Ce31d606
0 ETH0.000129452.03313351
Claim Vesting245026132026-02-21 3:41:112 hrs ago1771645271IN
0x5c3Acd26...2Ce31d606
0 ETH0.000013010.10795741
Claim Vesting245025702026-02-21 3:32:352 hrs ago1771644755IN
0x5c3Acd26...2Ce31d606
0 ETH0.000005150.04251069
Claim Vesting245025692026-02-21 3:32:232 hrs ago1771644743IN
0x5c3Acd26...2Ce31d606
0 ETH0.00000460.03801247
Claim Vesting245025642026-02-21 3:31:232 hrs ago1771644683IN
0x5c3Acd26...2Ce31d606
0 ETH0.000005380.04437032
Claim Vesting245025612026-02-21 3:30:472 hrs ago1771644647IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004740.03936713
Claim Vesting245025572026-02-21 3:29:592 hrs ago1771644599IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004370.0360895
Claim Vesting245025552026-02-21 3:29:352 hrs ago1771644575IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004670.03857536
Claim Vesting245025522026-02-21 3:28:592 hrs ago1771644539IN
0x5c3Acd26...2Ce31d606
0 ETH0.000004910.04052535
Claim Vesting245025472026-02-21 3:27:592 hrs ago1771644479IN
0x5c3Acd26...2Ce31d606
0 ETH0.0000050.04149637
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VestingClaimV2

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Claim} from "./Claim.sol";
import {IClaim} from "./IClaim.sol";
import {IVestingClaim} from "./IVestingClaim.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

// Errors
error NothingToClaim(); // No tokens available to claim at this time
error OnlyVestOption(); // User must have chosen Vest option in initial claim
error InvalidProof(); // Invalid merkle proof provided
error InvalidBeneficiaryCaller(); // Only beneficiary can claim for themselves
error InvalidGlobalEndTime(); // Global end time must be in the future or later than current setting
error CooldownNotExpired(); // Cooldown period has not expired
error V1MerkleRootNotSet(); // V1 contract merkle root must be set to expected value
error MaxCooldownEntriesReached(); // Must withdraw existing cooldown entries before claiming more

/**
 * @title VestingClaimV2
 * @notice Enhanced vesting contract with cooldown mechanism and existing contract compatibility
 * @dev This contract extends the original VestingClaim with:
 *      - Additional cooldown period after claiming vested tokens
 *      - Compatibility with existing VestingClaim contract state
 *      - Proper accounting for users who have already claimed from the original contract
 *
 * Key Features:
 * 1. Cooldown Mechanism: After claiming vested tokens, users must wait vestingCooldownDuration
 *    before being able to withdraw those tokens. This creates a two-step process:
 *    - Step 1: claimVesting() - claim vested tokens (starts cooldown)
 *    - Step 2: withdrawClaimed() - withdraw tokens after cooldown expires
 *
 * 2. Legacy Contract Integration: Reads state from the existing VestingClaim contract
 *    at 0x9E330c463a5B576f63D4aC92C6d54711D180E108 to properly account for
 *    users who have already partially claimed their vesting.
 *
 * 3. Multiple Cooldowns: Users can have multiple ongoing cooldowns from different claim events.
 */
contract VestingClaimV2 is IVestingClaim, Ownable2Step, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /* ------------- STRUCTS ------------- */
    struct CooldownEntry {
        uint256 amount;
        uint256 releaseTime;
    }

    /* ------------- IMMUTABLES ------------- */
    address public immutable token;
    Claim public immutable claimContract;
    IVestingClaim public immutable existingVestingContract;
    uint256 public immutable CLIFF_DURATION;
    uint256 public immutable VESTING_DURATION;
    uint256 public immutable vestingCooldownDuration;

    // Expected merkle root on V1 contract - V2 claims only allowed when V1 has this root set
    bytes32 public constant EXPECTED_V1_MERKLE_ROOT =
        0x74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e;

    // Maximum number of cooldown entries per account to prevent unbounded array growth
    uint256 public constant MAX_COOLDOWN_ENTRIES = 10;

    /* ------------- STATE VARIABLES ------------- */
    bytes32 public merkleRoot;
    uint256 public globalEndTime;

    // Track the verified total vesting amount for each user
    mapping(address => uint256) public totalVestingAmount;

    // Track how much each user has claimed from vesting (including legacy claims)
    mapping(address => uint256) public claimedVestingAmount;

    // Cooldown tracking: user => array of cooldown entries
    mapping(address => CooldownEntry[]) public cooldownEntries;

    // Track total amount in cooldown for each user
    mapping(address => uint256) public totalCooldownAmount;

    /* ------------- EVENTS ------------- */
    event TokensWithdrawn(address indexed account, uint256 amount, uint256 timestamp);

    /* ------------- CONSTRUCTOR ------------- */

    /**
     * @notice Initialize the VestingClaimV2 contract
     * @param _owner The owner of the contract
     * @param _token The token being vested
     * @param _claimContract The initial Claim contract address
     * @param _existingVestingContract The existing VestingClaim contract address
     * @param _merkleRoot The merkle root for vesting amounts
     * @param _cliffDuration The cliff duration in seconds (e.g., 30 days)
     * @param _vestingDuration The vesting duration in seconds (e.g., 180 days)
     * @param _globalEndTime The global end timestamp for vesting (must be in the future)
     * @param _vestingCooldownDuration The cooldown duration in seconds after claiming
     */
    constructor(
        address _owner,
        address _token,
        address _claimContract,
        address _existingVestingContract,
        bytes32 _merkleRoot,
        uint256 _cliffDuration,
        uint256 _vestingDuration,
        uint256 _globalEndTime,
        uint256 _vestingCooldownDuration
    ) Ownable(_owner) {
        require(_token != address(0), "token not set");
        require(_claimContract != address(0), "claimContract not set");
        require(_existingVestingContract != address(0), "existingVestingContract not set");
        require(_merkleRoot != bytes32(0), "merkleRoot not set");
        require(_cliffDuration > 0, "cliff duration must be > 0");
        require(_vestingDuration > 0, "vesting duration must be > 0");
        require(_globalEndTime > block.timestamp, "globalEndTime must be in future");
        require(_vestingCooldownDuration > 0, "cooldown duration must be > 0");

        token = _token;
        claimContract = Claim(_claimContract);
        existingVestingContract = IVestingClaim(_existingVestingContract);
        merkleRoot = _merkleRoot;
        CLIFF_DURATION = _cliffDuration;
        VESTING_DURATION = _vestingDuration;
        globalEndTime = _globalEndTime;
        vestingCooldownDuration = _vestingCooldownDuration;
    }

    /* ------------- WRITE FUNCTIONS ------------- */

    /**
     * @notice Claim vested tokens using merkle proof (starts cooldown)
     * @param index The index in the merkle tree
     * @param account The user address (must be msg.sender)
     * @param vestingAmount The total vesting amount for this user
     * @param merkleProof The merkle proof to verify the vesting amount
     */
    function claimVesting(uint256 index, address account, uint256 vestingAmount, bytes32[] calldata merkleProof)
        external
        nonReentrant
    {
        if (msg.sender != account) revert InvalidBeneficiaryCaller();

        // Check cooldown entries limit to prevent unbounded array growth
        if (cooldownEntries[account].length >= MAX_COOLDOWN_ENTRIES) {
            revert MaxCooldownEntriesReached();
        }

        // Safety check: ensure V1 contract has expected merkle root before allowing V2 claims
        if (existingVestingContract.merkleRoot() != EXPECTED_V1_MERKLE_ROOT) {
            revert V1MerkleRootNotSet();
        }

        // Verify user chose Vest option
        if (claimContract.claimedOptionMap(account) != IClaim.Option.Vest) {
            revert OnlyVestOption();
        }

        bytes32 node = keccak256(abi.encodePacked(index, account, vestingAmount));
        if (!MerkleProof.verify(merkleProof, merkleRoot, node)) {
            revert InvalidProof();
        }

        if (totalVestingAmount[account] != vestingAmount) {
            totalVestingAmount[account] = vestingAmount;
        }

        uint256 claimable = getClaimableAmount(account);
        if (claimable == 0) {
            revert NothingToClaim();
        }

        // Update claimed amount (from v2 contract)
        claimedVestingAmount[account] += claimable;

        // Add to cooldown instead of immediate transfer
        uint256 releaseTime = block.timestamp + vestingCooldownDuration;
        cooldownEntries[account].push(CooldownEntry({amount: claimable, releaseTime: releaseTime}));
        totalCooldownAmount[account] += claimable;

        emit VestingClaimed(account, claimable, block.timestamp);
    }

    /**
     * @notice Withdraw tokens from a specific cooldown entry that has completed its cooldown period
     * @dev Each claim creates one cooldown entry, and each entry must be withdrawn separately.
     *      This design prevents unbounded loops and ensures gas costs are predictable.
     * @param index The index of the cooldown entry to withdraw
     */
    function withdrawClaimed(uint256 index) external nonReentrant {
        CooldownEntry[] storage entries = cooldownEntries[msg.sender];
        require(index < entries.length, "Index out of bounds");

        CooldownEntry storage entry = entries[index];
        if (block.timestamp < entry.releaseTime) {
            revert CooldownNotExpired();
        }

        uint256 withdrawAmount = entry.amount;
        totalCooldownAmount[msg.sender] -= withdrawAmount;

        // Remove this entry by swapping with the last entry and popping
        entries[index] = entries[entries.length - 1];
        entries.pop();

        // Transfer tokens
        IERC20(token).safeTransfer(msg.sender, withdrawAmount);

        emit TokensWithdrawn(msg.sender, withdrawAmount, block.timestamp);
    }

    /**
     * @notice Update merkle root (owner only)
     * @param newRoot The new merkle root
     */
    function setMerkleRoot(bytes32 newRoot) external onlyOwner {
        merkleRoot = newRoot;
        emit MerkleRootUpdated(newRoot);
    }

    /**
     * @notice Owner can withdraw any ERC20 tokens sent to this contract
     * @param targetToken The token address to withdraw
     * @param amount The amount to withdraw
     */
    function withdrawERC20(address targetToken, uint256 amount) external onlyOwner {
        IERC20(targetToken).safeTransfer(owner(), amount);
    }

    /**
     * @notice Extend the global end time for vesting (owner only)
     * @dev Can only extend to a future time later than current setting
     * @param newEndTime The new global end timestamp
     */
    function setGlobalEndTime(uint256 newEndTime) external onlyOwner {
        if (newEndTime <= globalEndTime) revert InvalidGlobalEndTime();
        globalEndTime = newEndTime;
        emit GlobalEndTimeUpdated(newEndTime);
    }

    /* ------------- VIEW FUNCTIONS ------------- */

    /**
     * @notice Get the timestamp when cliff period ends for a user
     * @param account The user address
     * @return The timestamp when cliff ends (0 if user hasn't claimed)
     */
    function getCliffEnd(address account) public view returns (uint256) {
        uint256 claimTimestamp = claimContract.claimedTimestampMap(account);
        if (claimTimestamp == 0) {
            return 0;
        }
        return claimTimestamp + CLIFF_DURATION;
    }

    /**
     * @notice Get the timestamp when vesting period ends for a user
     * @param account The user address
     * @return The timestamp when vesting ends (0 if user hasn't claimed)
     */
    function getVestingEnd(address account) public view returns (uint256) {
        uint256 cliffEnd = getCliffEnd(account);
        if (cliffEnd == 0) {
            return 0;
        }
        return cliffEnd + VESTING_DURATION;
    }

    /**
     * @notice Calculate how much has vested for a user at current time
     * @dev Uses the stored vesting amount from registration
     * @param account The user address
     * @return The amount that has vested so far
     */
    function getVestedAmount(address account) public view returns (uint256) {
        uint256 userVestingAmount = totalVestingAmount[account];
        if (userVestingAmount == 0) return 0;
        return _getVestedAmount(account, userVestingAmount);
    }

    /**
     * @notice Calculate how much a user can claim right now (accounting for legacy claims)
     * @dev Uses the stored vesting amount from registration and legacy contract state
     * @param account The user address
     * @return The claimable amount
     */
    function getClaimableAmount(address account) public view returns (uint256) {
        uint256 vested = getVestedAmount(account);
        uint256 totalClaimed = getTotalClaimedAmount(account);
        return vested > totalClaimed ? vested - totalClaimed : 0;
    }

    /**
     * @notice Get total amount claimed by user (including from legacy contract)
     * @param account The user address
     * @return The total amount claimed across both contracts
     */
    function getTotalClaimedAmount(address account) public view returns (uint256) {
        uint256 legacyClaimed = existingVestingContract.claimedVestingAmount(account);
        uint256 currentClaimed = claimedVestingAmount[account];
        return legacyClaimed + currentClaimed;
    }

    /**
     * @notice Returns whether a user has registered their vesting amount
     * @dev Returns true if totalVestingAmount > 0 (set after first successful claim)
     * @param account The user address to check
     * @return True if user has completed at least one vesting claim
     */
    function isRegistered(address account) public view returns (bool) {
        return totalVestingAmount[account] > 0;
    }

    /**
     * @notice Get the number of active cooldown entries for a user
     * @param account The user address
     * @return The number of active cooldown entries
     */
    function getCooldownEntriesCount(address account) external view returns (uint256) {
        return cooldownEntries[account].length;
    }

    /**
     * @notice Get details of a specific cooldown entry
     * @param account The user address
     * @param index The index of the cooldown entry
     * @return amount The amount in cooldown
     * @return releaseTime The timestamp when tokens can be withdrawn
     */
    function getCooldownEntry(address account, uint256 index)
        external
        view
        returns (uint256 amount, uint256 releaseTime)
    {
        require(index < cooldownEntries[account].length, "Index out of bounds");
        CooldownEntry storage entry = cooldownEntries[account][index];
        return (entry.amount, entry.releaseTime);
    }

    /**
     * @notice Check if a specific cooldown entry is ready for withdrawal
     * @param account The user address
     * @param index The index of the cooldown entry
     * @return True if the entry's cooldown has expired and can be withdrawn
     */
    function isCooldownExpired(address account, uint256 index) external view returns (bool) {
        if (index >= cooldownEntries[account].length) {
            return false;
        }
        return block.timestamp >= cooldownEntries[account][index].releaseTime;
    }

    /* ------------- INTERNAL FUNCTIONS ------------- */

    /**
     * @dev Internal helper to calculate vested amount with a given total
     * @dev Takes into account globalEndTime (caps vesting at that time)
     * @param account The user address
     * @param _totalVestingAmount The total amount that will vest for this user
     * @return The amount that has vested so far
     */
    function _getVestedAmount(address account, uint256 _totalVestingAmount) internal view returns (uint256) {
        uint256 claimTimestamp = claimContract.claimedTimestampMap(account);
        if (claimTimestamp == 0) {
            return 0; // User hasn't claimed yet
        }

        // Check if cliff period has ended
        uint256 cliffEnd = claimTimestamp + CLIFF_DURATION;
        if (block.timestamp < cliffEnd) {
            return 0; // Still in cliff period
        }

        uint256 vestingEnd = cliffEnd + VESTING_DURATION;

        // Calculate effective end time (minimum of vestingEnd and globalEndTime)
        uint256 effectiveEnd = globalEndTime < vestingEnd ? globalEndTime : vestingEnd;

        // If effective end is at/before cliff, nothing vests
        if (effectiveEnd <= cliffEnd) {
            return 0;
        }

        // Time used for vesting calculation is capped at effectiveEnd
        uint256 timeForCalc = block.timestamp < effectiveEnd ? block.timestamp : effectiveEnd;

        // If we've reached the normal vesting end (and global end allows it), return full amount
        if (timeForCalc >= vestingEnd && effectiveEnd == vestingEnd) {
            return _totalVestingAmount;
        }

        // Linear vesting: calculate based on time elapsed since cliff
        uint256 timeElapsed = timeForCalc - cliffEnd;
        return (_totalVestingAmount * timeElapsed) / VESTING_DURATION;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IClaim} from "./IClaim.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

error AlreadyClaimed();
error InvalidProof();
error ClaimWindowNotStarted();
error ClaimWindowFinished();
error InvalidBeneficiaryCaller();
error InvalidOption();
error InvalidStakeAmount();

contract Claim is IClaim, Ownable2Step, ReentrancyGuard {
    using SafeERC20 for IERC20;

    address public immutable override token;
    address public immutable override stakedToken;
    bytes32 public override merkleRoot;
    uint256 public startTime;
    uint256 public endTime;

    uint256 public vestMultiplier;
    uint256 public noVestMultiplier;
    uint256 public constant multiplierDenominator = 100;

    mapping(uint256 => uint256) private claimedBitMap;
    mapping(address => uint256) public claimedTimestampMap;
    mapping(address => Option) public claimedOptionMap;

    constructor(
        address _owner,
        address _token,
        address _stakedToken,
        bytes32 _merkleRoot,
        uint256 _startTime,
        uint256 _endTime,
        uint256 _vestMultiplier,
        uint256 _noVestMultiplier
    ) Ownable(_owner) {
        require(_token != address(0), "token not set");
        require(_stakedToken != address(0), "stakedToken not set");
        require(_merkleRoot != bytes32(0), "merkleRoot not set");
        require(_startTime < _endTime, "endTime must be after startTime");
        require(_vestMultiplier >= 0 && _vestMultiplier <= multiplierDenominator, "vestMultiplier invalid");
        require(_noVestMultiplier >= 0 && _noVestMultiplier <= multiplierDenominator, "noVestMultiplier invalid");
        require(_vestMultiplier < _noVestMultiplier, "vestMultiplier must be less than noVestMultiplier");
        token = _token;
        stakedToken = _stakedToken;
        merkleRoot = _merkleRoot;
        startTime = _startTime;
        endTime = _endTime;
        vestMultiplier = _vestMultiplier;
        noVestMultiplier = _noVestMultiplier;
    }

    function isClaimed(uint256 index) public view override returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMap[claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }

    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        Option option,
        uint256 stakeAmount
    ) public virtual override nonReentrant {
        if (msg.sender != account) revert InvalidBeneficiaryCaller();
        if (block.timestamp < startTime) revert ClaimWindowNotStarted();
        if (block.timestamp > endTime) revert ClaimWindowFinished();
        if (isClaimed(index)) revert AlreadyClaimed();

        // Verify the merkle proof.
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        if (!MerkleProof.verify(merkleProof, merkleRoot, node)) {
            revert InvalidProof();
        }

        uint256 claimAmount = _getOptionMultiplier(option) * amount / multiplierDenominator;
        uint256 vestAmount = option == Option.Vest ? amount - claimAmount : 0;
        if (stakeAmount > claimAmount) revert InvalidStakeAmount();
        claimAmount = claimAmount - stakeAmount;

        // Mark it claimed and send the token.
        _setClaimed(index);
        _setClaimedMetadata(account, option);
        if (claimAmount > 0) {
            IERC20(token).safeTransfer(account, claimAmount);
        }
        if (stakeAmount > 0) {
            IERC20(token).approve(address(stakedToken), stakeAmount);
            IERC4626(stakedToken).deposit(stakeAmount, account);
        }

        emit Claimed(index, account, amount, claimAmount, stakeAmount, vestAmount, option);
    }

    function setMerkleRoot(bytes32 root) external onlyOwner {
        merkleRoot = root;
    }

    function withdraw(uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(owner(), amount);
    }

    function withdrawERC20(address targetToken, uint256 amount) external onlyOwner {
        IERC20(targetToken).safeTransfer(owner(), amount);
    }

    function _setClaimed(uint256 index) private {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
    }

    function _setClaimedMetadata(address account, Option option) private {
        claimedOptionMap[account] = option;
        claimedTimestampMap[account] = block.timestamp;
    }

    function _getOptionMultiplier(Option option) private view returns (uint256) {
        return option == Option.Vest ? vestMultiplier : noVestMultiplier;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

// Allows anyone to claim a token if they exist in a merkle root.
interface IClaim {
    // Enums for claim options
    enum Option {
        Vest,
        NoVest
    }

    // Returns the address of the token distributed by this contract.
    function token() external view returns (address);
    // Returns the address of the staked token
    function stakedToken() external view returns (address);
    // Returns the merkle root of the merkle tree containing account balances available to claim.
    function merkleRoot() external view returns (bytes32);
    // Returns true if the index has been marked claimed.
    function isClaimed(uint256 index) external view returns (bool);
    // Claim the given amount of the token to the given address. Reverts if the inputs are invalid.
    function claim(
        uint256 index,
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        Option option,
        uint256 stakeAmount
    ) external;

    // This event is triggered whenever a call to #claim succeeds.
    event Claimed(
        uint256 index,
        address indexed account,
        uint256 amount,
        uint256 claimAmount,
        uint256 stakeAmount,
        uint256 vestAmount,
        Option option
    );
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

// Interface for the VestingClaim contract
interface IVestingClaim {
    // Events
    event VestingClaimed(address indexed account, uint256 amount, uint256 timestamp);
    event MerkleRootUpdated(bytes32 newRoot);
    event GlobalEndTimeUpdated(uint256 newEndTime);

    /**
     * @notice Returns the address of the token being vested
     */
    function token() external view returns (address);

    /**
     * @notice Returns the merkle root for vesting amounts
     */
    function merkleRoot() external view returns (bytes32);

    /**
     * @notice Returns the cliff duration in seconds (30 days)
     */
    function CLIFF_DURATION() external view returns (uint256);

    /**
     * @notice Returns the vesting duration in seconds (180 days)
     */
    function VESTING_DURATION() external view returns (uint256);

    /**
     * @notice Returns the global end time for vesting (0 = no limit)
     */
    function globalEndTime() external view returns (uint256);

    /**
     * @notice Returns the total vesting amount for a user (set after first claim)
     * @param account The user address
     * @return The total amount that will vest for this user
     */
    function totalVestingAmount(address account) external view returns (uint256);

    /**
     * @notice Returns how much a user has already claimed from vesting
     * @param account The user address
     * @return The amount already claimed by the user
     */
    function claimedVestingAmount(address account) external view returns (uint256);

    /**
     * @notice Returns whether a user has registered their vesting amount
     * @param account The user address to check
     * @return True if user has completed at least one vesting claim
     */
    function isRegistered(address account) external view returns (bool);

    /**
     * @notice Get the timestamp when cliff period ends for a user
     * @param account The user address
     * @return The timestamp when cliff ends (0 if user hasn't claimed)
     */
    function getCliffEnd(address account) external view returns (uint256);

    /**
     * @notice Get the timestamp when vesting period ends for a user
     * @param account The user address
     * @return The timestamp when vesting ends (0 if user hasn't claimed)
     */
    function getVestingEnd(address account) external view returns (uint256);

    /**
     * @notice Calculate how much has vested for a user at current time
     * @param account The user address
     * @return The amount that has vested so far
     */
    function getVestedAmount(address account) external view returns (uint256);

    /**
     * @notice Calculate how much a user can claim right now
     * @param account The user address
     * @return The claimable amount
     */
    function getClaimableAmount(address account) external view returns (uint256);

    /**
     * @notice Claim vested tokens
     * @param index The index in the merkle tree
     * @param account The user address (must be msg.sender)
     * @param vestingAmount The total vesting amount for this user
     * @param merkleProof The merkle proof to verify the vesting amount
     */
    function claimVesting(uint256 index, address account, uint256 vestingAmount, bytes32[] calldata merkleProof)
        external;

    /**
     * @notice Update merkle root (owner only)
     * @param newRoot The new merkle root
     */
    function setMerkleRoot(bytes32 newRoot) external;

    /**
     * @notice Set or extend the global end time for vesting (owner only)
     * @param newEndTime The new global end timestamp (0 = no limit)
     */
    function setGlobalEndTime(uint256 newEndTime) external;

    /**
     * @notice Owner can withdraw any ERC20 tokens sent to this contract
     * @param targetToken The token address to withdraw
     * @param amount The amount to withdraw
     */
    function withdrawERC20(address targetToken, uint256 amount) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MerkleProof.sol)
// This file was procedurally generated from scripts/generate/templates/MerkleProof.js.

pragma solidity ^0.8.20;

import {Hashes} from "./Hashes.sol";

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 *
 * IMPORTANT: Consider memory side-effects when using custom hashing functions
 * that access memory in an unsafe way.
 *
 * NOTE: This library supports proof verification for merkle trees built using
 * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
 * leaf inclusion in trees built using non-commutative hashing functions requires
 * additional logic that is not supported by this library.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProof(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function processProof(
        bytes32[] memory proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProofCalldata(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function processProofCalldata(
        bytes32[] calldata proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

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

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProof(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

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

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

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

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

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

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     *
     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC4626.sol)

pragma solidity >=0.6.2;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)

pragma solidity >=0.6.2;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/Hashes.sol)

pragma solidity ^0.8.20;

/**
 * @dev Library of standard hash functions.
 *
 * _Available since v5.1._
 */
library Hashes {
    /**
     * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
     *
     * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
     */
    function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
        return a < b ? efficientKeccak256(a, b) : efficientKeccak256(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function efficientKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
        assembly ("memory-safe") {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity >=0.6.2;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 16 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 17 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)

pragma solidity >=0.4.16;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_claimContract","type":"address"},{"internalType":"address","name":"_existingVestingContract","type":"address"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_cliffDuration","type":"uint256"},{"internalType":"uint256","name":"_vestingDuration","type":"uint256"},{"internalType":"uint256","name":"_globalEndTime","type":"uint256"},{"internalType":"uint256","name":"_vestingCooldownDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CooldownNotExpired","type":"error"},{"inputs":[],"name":"InvalidBeneficiaryCaller","type":"error"},{"inputs":[],"name":"InvalidGlobalEndTime","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"MaxCooldownEntriesReached","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[],"name":"OnlyVestOption","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"V1MerkleRootNotSet","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newEndTime","type":"uint256"}],"name":"GlobalEndTimeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newRoot","type":"bytes32"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"TokensWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"VestingClaimed","type":"event"},{"inputs":[],"name":"CLIFF_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXPECTED_V1_MERKLE_ROOT","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_COOLDOWN_ENTRIES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VESTING_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimContract","outputs":[{"internalType":"contract Claim","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"vestingAmount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"claimVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimedVestingAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"cooldownEntries","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"releaseTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"existingVestingContract","outputs":[{"internalType":"contract IVestingClaim","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCliffEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCooldownEntriesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getCooldownEntry","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"releaseTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTotalClaimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVestedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVestingEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isCooldownExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newEndTime","type":"uint256"}],"name":"setGlobalEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalCooldownAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalVestingAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingCooldownDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"withdrawClaimed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101403461040c57601f61217538819003918201601f19168301916001600160401b03831184841017610410578084926101209460405283398101031261040c5761004981610424565b9061005660208201610424565b61006260408301610424565b9061006f60608401610424565b9060808401519060a08501519260c08601519461010060e08801519701519760018060a01b031680156103f957600180546001600160a01b03199081169091555f80549182168317815560405192916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a360016002556001600160a01b038416156103c757506001600160a01b0316908115610382576001600160a01b031691821561033d5783156103035784156102be57851561027957428711156102345787156101ef5760805260a05260c05260035560e0526101005260045561012052604051611d3c9081610439823960805181818161021e01526114d4015260a0518181816107b60152818161114e0152818161196a0152611b93015260c051818181610716015281816111bc01526117f7015260e051818181610c3a015281816119a70152611bd00152610100518181816112c7015281816118e10152611bff0152610120518181816108fb0152610dfa0152f35b60405162461bcd60e51b815260206004820152601d60248201527f636f6f6c646f776e206475726174696f6e206d757374206265203e20300000006044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f676c6f62616c456e6454696d65206d75737420626520696e20667574757265006044820152606490fd5b60405162461bcd60e51b815260206004820152601c60248201527f76657374696e67206475726174696f6e206d757374206265203e2030000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f636c696666206475726174696f6e206d757374206265203e20300000000000006044820152606490fd5b60405162461bcd60e51b81526020600482015260126024820152711b595c9adb19549bdbdd081b9bdd081cd95d60721b6044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f6578697374696e6756657374696e67436f6e7472616374206e6f7420736574006044820152606490fd5b60405162461bcd60e51b815260206004820152601560248201527f636c61696d436f6e7472616374206e6f742073657400000000000000000000006044820152606490fd5b62461bcd60e51b815260206004820152600d60248201526c1d1bdad95b881b9bdd081cd95d609a1b6044820152606490fd5b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361040c5756fe6080806040526004361015610012575f80fd5b5f3560e01c90816307484b58146115c857508063178073dd146113a05780632eb4a7ab14611365578063441c2749146112ea5780634cfc4d301461129257806350537466146112435780635aa00da1146111e0578063606c80151461117257806366345da414611104578063715018a61461104257806379ba509714610f3a5780637cb6475914610ed05780638da5cb5b14610e805780638fa8711014610e1d578063a0c1199514610dc5578063a0d2ce4e14610d62578063a1db978214610cfa578063a34b927414610c5d578063b3daa65214610c05578063b71aa54114610615578063bed7d586146105da578063c01114f0146105a1578063c3c5a5471461053c578063c4fc6664146104fb578063c82fdcf414610476578063d5a73fdd14610435578063e12f3a61146103f4578063e30c3978146103a3578063e4e53ca814610362578063e50efc8a146102ff578063f2fde38b14610242578063fc0c546a146101d45763fe866f8314610187575f80fd5b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c86101c361161e565b61190b565b604051908152f35b5f80fd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61028e61161e565b610296611b14565b16807fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015573ffffffffffffffffffffffffffffffffffffffff5f54167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a3005b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61034b61161e565b165f526005602052602060405f2054604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861039e61161e565b6118cc565b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861043061161e565b6118a9565b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861047161161e565b611877565b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576104ad61161e565b73ffffffffffffffffffffffffffffffffffffffff60243591165f52600760205260405f2080548210156101d0576040916104e791611641565b506001815491015482519182526020820152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861053761161e565b611797565b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61058861161e565b165f526005602052602060405f20541515604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020604051600a8152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020600454604051908152f35b346101d05760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760243573ffffffffffffffffffffffffffffffffffffffff8116908181036101d057604435906064359267ffffffffffffffff84116101d057366023850112156101d05783600401359267ffffffffffffffff84116101d0578360051b946024868201013681116101d0576106b76119fe565b833303610bdd57835f526007602052600a60405f20541015610bb5576040517f2eb4a7ab00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015610b30575f90610b63575b7f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e915003610b3b576040517f65688c2800000000000000000000000000000000000000000000000000000000815284600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610b30575f91610af5575b506002811015610ac857610aa057604051602081019060043582527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16604082015284605482015260548152610841607482611749565b519020916003549661085960206040519a018a611749565b8852602401602088015b828210610a9057505050925f935b86518510156108b35760208560051b88010151908181105f146108a2575f52602052600160405f205b940193610871565b905f52602052600160405f2061089a565b8503610a68576108d791835f5260056020528060405f205403610a55575b506118a9565b8015610a2d57815f52600660205260405f206108f482825461178a565b90556109207f00000000000000000000000000000000000000000000000000000000000000004261178a565b825f52600760205260405f2091604051926040840184811067ffffffffffffffff821117610a005760405281845260208401928352805468010000000000000000811015610a005761097791600182018155611641565b6109d45760016040937f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b517219551835551910155835f526008602052815f206109bf82825461178a565b90558151908152426020820152a26001600255005b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f969bf728000000000000000000000000000000000000000000000000000000005f5260045ffd5b835f52600560205260405f2055836108d1565b7f09bde339000000000000000000000000000000000000000000000000000000005f5260045ffd5b8135815260209182019101610863565b7fee226224000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b90506020813d602011610b28575b81610b1060209383611749565b810103126101d0575160028110156101d057886107e6565b3d9150610b03565b6040513d5f823e3d90fd5b7f7fad7dfb000000000000000000000000000000000000000000000000000000005f5260045ffd5b506020813d602011610bad575b81610b7d60209383611749565b810103126101d0577f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e9051610745565b3d9150610b70565b7f1bf91960000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbea619b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057600435610c97611b14565b600454811115610cd2576020817f18779e6ee1837a84cc0e45aa1a8e1320374da6a0ee57437d4da82e68747aa9fa92600455604051908152a1005b7fcdef05b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057610d60610d3461161e565b610d3c611b14565b6024359073ffffffffffffffffffffffffffffffffffffffff805f54169116611a35565b005b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff610dae61161e565b165f526006602052602060405f2054604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff610e6961161e565b165f526007602052602060405f2054604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0577f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea9416020600435610f2d611b14565b80600355604051908152a1005b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0573373ffffffffffffffffffffffffffffffffffffffff6001541603611016577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154166001555f54337fffffffffffffffffffffffff00000000000000000000000000000000000000008216175f5573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057611078611b14565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000600154166001555f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61122c61161e565b165f526008602052602060405f2054604051908152f35b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602061128861127f61161e565b602435906116f9565b6040519015158152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760406104e761132661161e565b73ffffffffffffffffffffffffffffffffffffffff6024359116805f526007602052611356845f20548310611687565b5f526007602052825f20611641565b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020600354604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576004356113da6119fe565b335f52600760205260405f206113f281548310611687565b6113fc8282611641565b50600181015442106115a0575491335f52600860205260405f206114218482546116ec565b905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116115735761145c6114649184611641565b509183611641565b9190916109d45780820361155f575b505080548015611532577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01906114aa8282611641565b6109d4576001815f8093550155556114f9813373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016611a35565b6040519081524260208201527fffe903c0abe6b2dbb2f3474ef43d7a3c1fca49e5a774453423ca8e1952aabffa60403392a26001600255005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b600181819254845501549101558280611473565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f3ce33ddb000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057807f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e60209252f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101d057565b805482101561165a575f5260205f209060011b01905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b1561168e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e646578206f7574206f6620626f756e6473000000000000000000000000006044820152fd5b9190820391821161157357565b73ffffffffffffffffffffffffffffffffffffffff16805f52600760205260405f20548210156117435760019161173a915f52600760205260405f20611641565b50015442101590565b50505f90565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a0057604052565b9190820180921161157357565b73ffffffffffffffffffffffffffffffffffffffff604051917fa0d2ce4e0000000000000000000000000000000000000000000000000000000083521680600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610b30575f91611841575b61183e92505f52600660205260405f20549061178a565b90565b90506020823d60201161186f575b8161185c60209383611749565b810103126101d05761183e915190611827565b3d915061184f565b73ffffffffffffffffffffffffffffffffffffffff81165f52600560205260405f205480156117435761183e91611b34565b6118bb6118b582611877565b91611797565b808211156117435761183e916116ec565b6118d59061190b565b80156119065761183e907f00000000000000000000000000000000000000000000000000000000000000009061178a565b505f90565b73ffffffffffffffffffffffffffffffffffffffff604051917f7d5948f600000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610b30575f916119cc575b5080156119065761183e907f00000000000000000000000000000000000000000000000000000000000000009061178a565b90506020813d6020116119f6575b816119e760209383611749565b810103126101d057515f61199a565b3d91506119da565b6002805414611a0d5760028055565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b916020915f916040519073ffffffffffffffffffffffffffffffffffffffff858301937fa9059cbb000000000000000000000000000000000000000000000000000000008552166024830152604482015260448152611a95606482611749565b519082855af115610b30575f513d611b0b575073ffffffffffffffffffffffffffffffffffffffff81163b155b611ac95750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415611ac2565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361101657565b73ffffffffffffffffffffffffffffffffffffffff604051917f7d5948f600000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610b30575f91611cd4575b50801561174357611bf5907f00000000000000000000000000000000000000000000000000000000000000009061178a565b90814210611743577f000000000000000000000000000000000000000000000000000000000000000091611c29838261178a565b60045481811015611ccd575b82811115611cc4578042105f14611cbd5742915b808310159182611cb3575b5050611cac5790611c64916116ec565b90818102918183041490151715611573578115611c7f570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5050905090565b1490505f80611c54565b8091611c49565b50505050505f90565b5080611c35565b90506020813d602011611cfe575b81611cef60209383611749565b810103126101d057515f611bc3565b3d9150611ce256fea2646970667358221220557ee68cd37af789209130256e1c53fa0f525097a9436e4136236798d4fd668064736f6c634300081e0033000000000000000000000000bb5e1337a2ac59f0f63120d38aa865cf12844153000000000000000000000000fa1c09fc8b491b6a4d3ff53a10cad29381b3f9490000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e30000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e10808fb08894c650df25a868e10d89e3422a025210b3670690840efa5cdad1be2180000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000ed4e00000000000000000000000000000000000000000000000000000000006a1a288000000000000000000000000000000000000000000000000000000000003b5380

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f3560e01c90816307484b58146115c857508063178073dd146113a05780632eb4a7ab14611365578063441c2749146112ea5780634cfc4d301461129257806350537466146112435780635aa00da1146111e0578063606c80151461117257806366345da414611104578063715018a61461104257806379ba509714610f3a5780637cb6475914610ed05780638da5cb5b14610e805780638fa8711014610e1d578063a0c1199514610dc5578063a0d2ce4e14610d62578063a1db978214610cfa578063a34b927414610c5d578063b3daa65214610c05578063b71aa54114610615578063bed7d586146105da578063c01114f0146105a1578063c3c5a5471461053c578063c4fc6664146104fb578063c82fdcf414610476578063d5a73fdd14610435578063e12f3a61146103f4578063e30c3978146103a3578063e4e53ca814610362578063e50efc8a146102ff578063f2fde38b14610242578063fc0c546a146101d45763fe866f8314610187575f80fd5b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c86101c361161e565b61190b565b604051908152f35b5f80fd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fa1c09fc8b491b6a4d3ff53a10cad29381b3f949168152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61028e61161e565b610296611b14565b16807fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015573ffffffffffffffffffffffffffffffffffffffff5f54167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a3005b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61034b61161e565b165f526005602052602060405f2054604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861039e61161e565b6118cc565b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861043061161e565b6118a9565b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861047161161e565b611877565b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576104ad61161e565b73ffffffffffffffffffffffffffffffffffffffff60243591165f52600760205260405f2080548210156101d0576040916104e791611641565b506001815491015482519182526020820152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206101c861053761161e565b611797565b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61058861161e565b165f526005602052602060405f20541515604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020604051600a8152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020600454604051908152f35b346101d05760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760243573ffffffffffffffffffffffffffffffffffffffff8116908181036101d057604435906064359267ffffffffffffffff84116101d057366023850112156101d05783600401359267ffffffffffffffff84116101d0578360051b946024868201013681116101d0576106b76119fe565b833303610bdd57835f526007602052600a60405f20541015610bb5576040517f2eb4a7ab00000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e108165afa8015610b30575f90610b63575b7f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e915003610b3b576040517f65688c2800000000000000000000000000000000000000000000000000000000815284600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e3165afa908115610b30575f91610af5575b506002811015610ac857610aa057604051602081019060043582527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16604082015284605482015260548152610841607482611749565b519020916003549661085960206040519a018a611749565b8852602401602088015b828210610a9057505050925f935b86518510156108b35760208560051b88010151908181105f146108a2575f52602052600160405f205b940193610871565b905f52602052600160405f2061089a565b8503610a68576108d791835f5260056020528060405f205403610a55575b506118a9565b8015610a2d57815f52600660205260405f206108f482825461178a565b90556109207f00000000000000000000000000000000000000000000000000000000003b53804261178a565b825f52600760205260405f2091604051926040840184811067ffffffffffffffff821117610a005760405281845260208401928352805468010000000000000000811015610a005761097791600182018155611641565b6109d45760016040937f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b517219551835551910155835f526008602052815f206109bf82825461178a565b90558151908152426020820152a26001600255005b7f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f969bf728000000000000000000000000000000000000000000000000000000005f5260045ffd5b835f52600560205260405f2055836108d1565b7f09bde339000000000000000000000000000000000000000000000000000000005f5260045ffd5b8135815260209182019101610863565b7fee226224000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b90506020813d602011610b28575b81610b1060209383611749565b810103126101d0575160028110156101d057886107e6565b3d9150610b03565b6040513d5f823e3d90fd5b7f7fad7dfb000000000000000000000000000000000000000000000000000000005f5260045ffd5b506020813d602011610bad575b81610b7d60209383611749565b810103126101d0577f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e9051610745565b3d9150610b70565b7f1bf91960000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fbea619b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f0000000000000000000000000000000000000000000000000000000000278d008152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057600435610c97611b14565b600454811115610cd2576020817f18779e6ee1837a84cc0e45aa1a8e1320374da6a0ee57437d4da82e68747aa9fa92600455604051908152a1005b7fcdef05b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057610d60610d3461161e565b610d3c611b14565b6024359073ffffffffffffffffffffffffffffffffffffffff805f54169116611a35565b005b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff610dae61161e565b165f526006602052602060405f2054604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f00000000000000000000000000000000000000000000000000000000003b53808152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff610e6961161e565b165f526007602052602060405f2054604051908152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0577f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea9416020600435610f2d611b14565b80600355604051908152a1005b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0573373ffffffffffffffffffffffffffffffffffffffff6001541603611016577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154166001555f54337fffffffffffffffffffffffff00000000000000000000000000000000000000008216175f5573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057611078611b14565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000600154166001555f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e3168152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e108168152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05773ffffffffffffffffffffffffffffffffffffffff61122c61161e565b165f526008602052602060405f2054604051908152f35b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057602061128861127f61161e565b602435906116f9565b6040519015158152f35b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760206040517f0000000000000000000000000000000000000000000000000000000000ed4e008152f35b346101d05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d05760406104e761132661161e565b73ffffffffffffffffffffffffffffffffffffffff6024359116805f526007602052611356845f20548310611687565b5f526007602052825f20611641565b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576020600354604051908152f35b346101d05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d0576004356113da6119fe565b335f52600760205260405f206113f281548310611687565b6113fc8282611641565b50600181015442106115a0575491335f52600860205260405f206114218482546116ec565b905581547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116115735761145c6114649184611641565b509183611641565b9190916109d45780820361155f575b505080548015611532577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01906114aa8282611641565b6109d4576001815f8093550155556114f9813373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fa1c09fc8b491b6a4d3ff53a10cad29381b3f94916611a35565b6040519081524260208201527fffe903c0abe6b2dbb2f3474ef43d7a3c1fca49e5a774453423ca8e1952aabffa60403392a26001600255005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b600181819254845501549101558280611473565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f3ce33ddb000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101d0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101d057807f74d3ba498e1223f77287fab2d904e994ca03b63336d5b384eee2335b237d573e60209252f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101d057565b805482101561165a575f5260205f209060011b01905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b1561168e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f496e646578206f7574206f6620626f756e6473000000000000000000000000006044820152fd5b9190820391821161157357565b73ffffffffffffffffffffffffffffffffffffffff16805f52600760205260405f20548210156117435760019161173a915f52600760205260405f20611641565b50015442101590565b50505f90565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a0057604052565b9190820180921161157357565b73ffffffffffffffffffffffffffffffffffffffff604051917fa0d2ce4e0000000000000000000000000000000000000000000000000000000083521680600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e108165afa908115610b30575f91611841575b61183e92505f52600660205260405f20549061178a565b90565b90506020823d60201161186f575b8161185c60209383611749565b810103126101d05761183e915190611827565b3d915061184f565b73ffffffffffffffffffffffffffffffffffffffff81165f52600560205260405f205480156117435761183e91611b34565b6118bb6118b582611877565b91611797565b808211156117435761183e916116ec565b6118d59061190b565b80156119065761183e907f0000000000000000000000000000000000000000000000000000000000ed4e009061178a565b505f90565b73ffffffffffffffffffffffffffffffffffffffff604051917f7d5948f600000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e3165afa908115610b30575f916119cc575b5080156119065761183e907f0000000000000000000000000000000000000000000000000000000000278d009061178a565b90506020813d6020116119f6575b816119e760209383611749565b810103126101d057515f61199a565b3d91506119da565b6002805414611a0d5760028055565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b916020915f916040519073ffffffffffffffffffffffffffffffffffffffff858301937fa9059cbb000000000000000000000000000000000000000000000000000000008552166024830152604482015260448152611a95606482611749565b519082855af115610b30575f513d611b0b575073ffffffffffffffffffffffffffffffffffffffff81163b155b611ac95750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415611ac2565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361101657565b73ffffffffffffffffffffffffffffffffffffffff604051917f7d5948f600000000000000000000000000000000000000000000000000000000835216600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e3165afa908115610b30575f91611cd4575b50801561174357611bf5907f0000000000000000000000000000000000000000000000000000000000278d009061178a565b90814210611743577f0000000000000000000000000000000000000000000000000000000000ed4e0091611c29838261178a565b60045481811015611ccd575b82811115611cc4578042105f14611cbd5742915b808310159182611cb3575b5050611cac5790611c64916116ec565b90818102918183041490151715611573578115611c7f570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5050905090565b1490505f80611c54565b8091611c49565b50505050505f90565b5080611c35565b90506020813d602011611cfe575b81611cef60209383611749565b810103126101d057515f611bc3565b3d9150611ce256fea2646970667358221220557ee68cd37af789209130256e1c53fa0f525097a9436e4136236798d4fd668064736f6c634300081e0033

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

000000000000000000000000bb5e1337a2ac59f0f63120d38aa865cf12844153000000000000000000000000fa1c09fc8b491b6a4d3ff53a10cad29381b3f9490000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e30000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e10808fb08894c650df25a868e10d89e3422a025210b3670690840efa5cdad1be2180000000000000000000000000000000000000000000000000000000000278d000000000000000000000000000000000000000000000000000000000000ed4e00000000000000000000000000000000000000000000000000000000006a1a288000000000000000000000000000000000000000000000000000000000003b5380

-----Decoded View---------------
Arg [0] : _owner (address): 0xBB5E1337A2Ac59F0F63120d38aA865cf12844153
Arg [1] : _token (address): 0xFA1C09fC8B491B6A4d3Ff53A10CAd29381b3F949
Arg [2] : _claimContract (address): 0x1178c6a8E2600F0E9baFbC5e5677fE10805f05e3
Arg [3] : _existingVestingContract (address): 0x9E330c463a5B576f63D4aC92C6d54711D180E108
Arg [4] : _merkleRoot (bytes32): 0x08fb08894c650df25a868e10d89e3422a025210b3670690840efa5cdad1be218
Arg [5] : _cliffDuration (uint256): 2592000
Arg [6] : _vestingDuration (uint256): 15552000
Arg [7] : _globalEndTime (uint256): 1780099200
Arg [8] : _vestingCooldownDuration (uint256): 3888000

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000bb5e1337a2ac59f0f63120d38aa865cf12844153
Arg [1] : 000000000000000000000000fa1c09fc8b491b6a4d3ff53a10cad29381b3f949
Arg [2] : 0000000000000000000000001178c6a8e2600f0e9bafbc5e5677fe10805f05e3
Arg [3] : 0000000000000000000000009e330c463a5b576f63d4ac92c6d54711d180e108
Arg [4] : 08fb08894c650df25a868e10d89e3422a025210b3670690840efa5cdad1be218
Arg [5] : 0000000000000000000000000000000000000000000000000000000000278d00
Arg [6] : 0000000000000000000000000000000000000000000000000000000000ed4e00
Arg [7] : 000000000000000000000000000000000000000000000000000000006a1a2880
Arg [8] : 00000000000000000000000000000000000000000000000000000000003b5380


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
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.