ETH Price: $3,310.21 (-0.74%)

Contract

0x72854FBb44d3dd87109D46a9298AEB0d018740f0
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw216282362025-01-15 6:57:1132 hrs ago1736924231IN
0x72854FBb...d018740f0
0 ETH0.000345533.11162357
Withdraw216232022025-01-14 14:03:592 days ago1736863439IN
0x72854FBb...d018740f0
0 ETH0.0010241210.90122864
Withdraw215866332025-01-09 11:30:117 days ago1736422211IN
0x72854FBb...d018740f0
0 ETH0.000488585.2
Deposit215182172024-12-30 22:16:2316 days ago1735596983IN
0x72854FBb...d018740f0
0 ETH0.0018730910.03319117
Withdraw215179762024-12-30 21:28:1116 days ago1735594091IN
0x72854FBb...d018740f0
0 ETH0.000716127.62269611
Deposit215149072024-12-30 11:11:2317 days ago1735557083IN
0x72854FBb...d018740f0
0 ETH0.001055365.51099771
Withdraw214545102024-12-22 0:39:2325 days ago1734827963IN
0x72854FBb...d018740f0
0 ETH0.000947378.53136422
Withdraw214172642024-12-16 19:46:3530 days ago1734378395IN
0x72854FBb...d018740f0
0 ETH0.0029683926.73123484
Withdraw213909832024-12-13 3:44:2334 days ago1734061463IN
0x72854FBb...d018740f0
0 ETH0.001414212.73532959
Withdraw213723552024-12-10 13:19:2337 days ago1733836763IN
0x72854FBb...d018740f0
0 ETH0.0030208727.20086237
Withdraw213656192024-12-09 14:43:1138 days ago1733755391IN
0x72854FBb...d018740f0
0 ETH0.0041236537.13466002
Withdraw213624032024-12-09 3:58:2338 days ago1733716703IN
0x72854FBb...d018740f0
0 ETH0.0011649312.4
Withdraw213489652024-12-07 6:56:1140 days ago1733554571IN
0x72854FBb...d018740f0
0 ETH0.0012266611.04644545
Withdraw213476732024-12-07 2:36:4740 days ago1733539007IN
0x72854FBb...d018740f0
0 ETH0.0011011111.71919956
Withdraw213403442024-12-06 2:02:3541 days ago1733450555IN
0x72854FBb...d018740f0
0 ETH0.0021715919.55373291
Withdraw213259242024-12-04 1:42:1143 days ago1733276531IN
0x72854FBb...d018740f0
0 ETH0.0018314519.49228654
Withdraw213121882024-12-02 3:37:3545 days ago1733110655IN
0x72854FBb...d018740f0
0 ETH0.0017072615.37436425
Withdraw212581392024-11-24 14:15:5953 days ago1732457759IN
0x72854FBb...d018740f0
0 ETH0.0010741111.43328473
Withdraw212269992024-11-20 5:55:5957 days ago1732082159IN
0x72854FBb...d018740f0
0 ETH0.000949128.54618598
Withdraw212206772024-11-19 8:47:3558 days ago1732006055IN
0x72854FBb...d018740f0
0 ETH0.000841318.95528682
Withdraw211617702024-11-11 3:32:1166 days ago1731295931IN
0x72854FBb...d018740f0
0 ETH0.0021287319.16982739
Withdraw211076632024-11-03 14:18:2374 days ago1730643503IN
0x72854FBb...d018740f0
0 ETH0.0013710312.34517604
Withdraw210830482024-10-31 3:51:3577 days ago1730346695IN
0x72854FBb...d018740f0
0 ETH0.000904529.62692711
Withdraw210366782024-10-24 16:33:2383 days ago1729787603IN
0x72854FBb...d018740f0
0 ETH0.0021569519.42394167
Withdraw210265232024-10-23 6:34:1185 days ago1729665251IN
0x72854FBb...d018740f0
0 ETH0.000545984.91677644
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AirdropSingleSidedStaking

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 2 runs

Other Settings:
paris EvmVersion
File 1 of 19 : AirdropSingleSidedStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "./interfaces/IAirdropSingleSidedStaking.sol";

import "./ArcadeSingleSidedStaking.sol";

import { ASS_CallerNotAirdropDistribution } from "../src/errors/SingleSidedStaking.sol";

contract AirdropSingleSidedStaking is IAirdropSingleSidedStaking, ArcadeSingleSidedStaking {
    address public airdropDistribution;

    /**
     * @notice Sets up the contract by initializing the deposit token and the owner.
     *
     * @param _owner                       The address of the contract owner.
     * @param _arcd                        The address of the deposit ERC20 token.
     * @param _airdropDistribution         The address of the airdrop distributor contract.
     */
    constructor(
        address _owner,
        address _arcd,
        address _airdropDistribution
    ) ArcadeSingleSidedStaking(_owner, _arcd) {
        if (address(_airdropDistribution) == address(0)) revert ASS_ZeroAddress("airdropDistribution");

        airdropDistribution = _airdropDistribution;
    }

    modifier onlyAirdropDistribution() {
        if(msg.sender != airdropDistribution) revert ASS_CallerNotAirdropDistribution();
        _;
    }

    /** @notice Receives an airdrop for a specific recipient with a specified amount, delegation,
     *          and lock period. This function is restricted to be called by the airdrop distribution
     *          account only and will call the internal `_deposit` function to handle the token
     *          transfer and voting power allocation.
     *
     * @param recipient                     The address of the user who will receive the airdropped tokens.
     * @param amount                        The amount of tokens that will be airdropped to the user.
     * @param delegation                    The address of the user's delegatee.
     * @param lock                          The lock period for the airdropped tokens.
     */
    function airdropReceive(
        address recipient,
        uint256 amount,
        address delegation,
        Lock lock
    ) external onlyAirdropDistribution {
        _deposit(recipient, amount, delegation, lock);
    }

    /** @notice Sets the airdrop distribution account that is allowed to call `airdropReceive`.
     *
     * @param _airdropDistribution            The address allowed caller.
     */
    function setAirdropDistribution(address _airdropDistribution) external onlyOwner {
        airdropDistribution = _airdropDistribution;

        emit AirdropDistributionSet(airdropDistribution);
    }
}

File 2 of 19 : IAirdropSingleSidedStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "./IArcadeSingleSidedStaking.sol";

interface IAirdropSingleSidedStaking is  IArcadeSingleSidedStaking {
    event AirdropDistributionSet(address indexed airdropDistribution);

    function airdropReceive(
        address recipient,
        uint256 amount,
        address delegation,
        Lock lock
    ) external;

    function setAirdropDistribution(address _airdropDistribution) external;

    function airdropDistribution() external view returns (address);
}

File 3 of 19 : ArcadeSingleSidedStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "./external/council/interfaces/IVotingVault.sol";
import "./external/council/libraries/History.sol";
import "./external/council/libraries/Storage.sol";

import "./interfaces/IArcadeSingleSidedStaking.sol";

import {
    ASS_ZeroAddress,
    ASS_ZeroAmount,
    ASS_DepositToken,
    ASS_BalanceAmount,
    ASS_Locked,
    ASS_DepositCountExceeded,
    ASS_InvalidDelegationAddress,
    ASS_AmountTooBig
} from "../src/errors/SingleSidedStaking.sol";

/**
 * @title ArcadeSingleSidedStaking
 * @author Non-Fungible Technologies, Inc.
 *
 * @notice To optimize gas usage, unlockTimeStamp in struct UserDeposit is stored in
 *         uint32 format. This limits timestamp support to dates before 03:14:07 UTC on
 *         19 January 2038. Any time beyond this point will cause an overflow.
 *
 * The ArcadeSingleSidedStaking contract is set up like a traditional staking contract,
 * but with a twist: instead of earning tokens as rewards, users deposit their ARCD tokens
 * in the contract and get d’App points in return. Earned points are tallied up off-chain
 * and account towards the $ARCD Rewards program and its Levels.
 *
 * Upon depositing, users are required to commit to a lock period where tokens are
 * immovable, until the chosen lock period expires. Early  withdrawal is not permitted.
 * Users have the flexibility to make multiple deposits, each accruing points separately
 * until their lock period concludes.
 *
 * Should users choose not to withdraw their tokens post the lock period, the
 * funds will seamlessly transition into a subsequent points tracking cycle if
 * one should start. Unlike the initial deposit, the funds in the consequent point
 * tracking cycles are not bound by a lock period and can be freely withdrawn anytime.
 * Tracking cycles are defined and tracked in the dApp.
 *
 * The lock period gives users the opportunity to enhance their points earnings
 * with bonus multipliers that are contingent on the duration for which the user
 * chooses to lock their deposited tokens. These bonus calculations are tallied offchain.
 * The available lock durations are categorized as short, medium, and long. Each category
 * is associated with a progressively increasing multiplier that enhances the number of
 * point rewards accrued in the d'App, with the short duration offering the smallest and
 * the long duration offering the largest.
 *
 * In the exitAll() external function, it's necessary to limit the number of
 * processed transactions within the function's loops to prevent exceeding
 * the block gas limit. Because of this, the contract enforces a hard limit
 * on the number deposits a user can have per wallet address and consequently
 * on the number of iterations that can be processed in a single transaction.
 * This limit is defined by the MAX_DEPOSITS state variable. Should a user
 * necessitate making more than the MAX_DEPOSITS  number of deposits, they will
 * be required to use a different wallet address.
 *
 * The contract gives users governance capabilities by also serving as a voting
 * vault. When users deposit, they gain voting power which they can use in
 * ArcadeDAO governance. Users' voting power is automatically accrued to their account
 * and is delegated to their chosen delegatee's address on their behalf without the
 * need for them to call any additional transaction.
 * The ArcadeSingleSidedStaking contract governance functionality is adapted from the
 * Council LockingVault deployment at:
 * https://etherscan.io/address/0x7a58784063D41cb78FBd30d271F047F0b9156d6e#code
 *
 * Once a user makes their initial deposit, the voting power for any future deposits
 * will need to be delegated to the same address as in the initial deposit. To assign
 * a different delegate, users are required to use the changeDelegate() function.
 * A user's voting power is determined by the quantity of ARCD tokens they have deposited.
 */

contract ArcadeSingleSidedStaking is IArcadeSingleSidedStaking, IVotingVault, ReentrancyGuard, Ownable, Pausable {
    using SafeERC20 for IERC20;

    // Bring library into scope
    using History for History.HistoricalBalances;

    // ============================================ STATE ==============================================
    // ============== Constants ==============
    uint256 public constant MAX_DEPOSITS = 20;
    uint256 public constant SHORT_LOCK_TIME = 30 days;
    uint256 public constant MEDIUM_LOCK_TIME = 60 days;
    uint256 public constant LONG_LOCK_TIME = 150 days;

    // ============ Global State =============
    IERC20 public immutable arcd;

    mapping(address => UserDeposit[]) public deposits;

    uint256 public totalDeposits;

    // ========================================== CONSTRUCTOR ===========================================
    /**
     * @notice Sets up the contract by initializing the deposit token and setting the owner.
     *
     * @param _owner                       The address of the contract owner.
     * @param _arcd                        The address of the deposit ERC20 token.
     */
    constructor(
        address _owner,
        address _arcd
    ) Ownable(_owner) {
        if (address(_arcd) == address(0)) revert ASS_ZeroAddress("arcd");

        arcd = IERC20(_arcd);
    }

    // ========================================== VIEW FUNCTIONS =========================================
    /**
     * @notice Returns the total amount of deposited tokens held in the contract.
     *
     * @return uint256                     The amount of deposited tokens.
     */
    function totalSupply() external view returns (uint256) {
        return totalDeposits;
    }

    /**
     * @notice Returns the amount of tokens deposited by a user account.
     *
     * @param account                       The address of the account.
     *
     * @return userBalance                  The total amount that the user has deposited.
     */
    function getTotalUserDeposits(address account) external view returns (uint256 userBalance) {
        UserDeposit[] storage userDeposits = deposits[account];

        uint256 numUserDeposits = userDeposits.length;
        for (uint256 i = 0; i < numUserDeposits; ++i) {
            UserDeposit storage userDeposit = userDeposits[i];
            userBalance += userDeposit.amount;
        }
    }

    /**
     * @notice Returns the amount of deposited tokens pertaining to a specific deposit.
     *
     * @param account                       The address of the account.
     * @param depositId                     The specified deposit to get the balance of.
     *
     * @return depositBalance               The total amount committed to the deposit.
     */
    function balanceOfDeposit(address account, uint256 depositId) external view returns (uint256 depositBalance) {
        depositBalance = deposits[account][depositId].amount;
    }

    /**
     * @notice Returns information about a deposit.

     * @param account                           The user whose deposit to get.
     * @param depositId                         The specified deposit to get.
     *
     * @return lock                             Lock period committed.
     * @return unlockTimestamp                  Timestamp marking the end of the lock period.
     * @return amount                           Amount deposited.
     */
    function getUserDeposit(address account, uint256 depositId)
        external
        view
        returns (uint8 lock, uint32 unlockTimestamp, uint256 amount)
    {
        UserDeposit storage userDeposit = deposits[account][depositId];

        lock = uint8(userDeposit.lock);
        unlockTimestamp = userDeposit.unlockTimestamp;
        amount = userDeposit.amount;
    }

    /**
     * @notice Returns the last depositId, equivalent to userDeposits.length.
     *
     * @param account                           The user whose last deposit to get.
     *
     * @return lastDepositId                    Id of the last deposit.
     */
    function getLastDepositId(address account) external view returns (uint256 lastDepositId) {
        lastDepositId = deposits[account].length - 1;
    }

    /**
     * @notice Gets all of a user's active deposits.
     *
     * @param account                           The user whose deposits to get.
     *
     * @return activeDeposits                   Array of id's of the user's active deposits.
     */
    function getActiveDeposits(address account) external view returns (uint256[] memory) {
        UserDeposit[] storage userDeposits = deposits[account];
        uint256 activeCount = 0;

        uint256 numUserDeposits = userDeposits.length;
        for (uint256 i = 0; i < numUserDeposits; ++i) {
            UserDeposit storage userDeposit = userDeposits[i];
            if (userDeposit.amount > 0) {
                activeCount++;
            }
        }

        uint256[] memory activeDeposits = new uint256[](activeCount);
        uint256 activeIndex;

        for (uint256 i = 0; i < numUserDeposits; ++i) {
            if (userDeposits[i].amount > 0) {
                activeDeposits[activeIndex] = i;
                activeIndex++;
            }
        }

        return activeDeposits;
    }

    // ========================================= MUTATIVE FUNCTIONS ========================================
    /**
     * @notice Allows users to deposit their tokens, which are then tracked in the contract.
     *
     * @param amount                           The amount of tokens the user wishes to deposit and lock.
     * @param delegation                       The address to which the user's voting power will be delegated.
     * @param lock                             The chosen locking period for the deposited tokens.
     */
    function deposit(
        uint256 amount,
        address delegation,
        Lock lock
    ) external {
        _deposit(msg.sender, amount, delegation, lock);
    }

    /**
     * @notice Withdraws deposited tokens that are unlocked.  Allows for partial withdrawals.
     *
     * @param depositId                        The specified deposit to withdraw from.
     * @param amount                           The amount to be withdrawn.
     */
    function withdraw(uint256 amount, uint256 depositId) public whenNotPaused nonReentrant {
        if (amount == 0) revert ASS_ZeroAmount();
        UserDeposit storage userDeposit = deposits[msg.sender][depositId];
        if (userDeposit.amount == 0) revert ASS_BalanceAmount();
        if (block.timestamp < userDeposit.unlockTimestamp) revert ASS_Locked();

        if (amount > userDeposit.amount) amount = userDeposit.amount;

        _subtractVotingPower(amount, msg.sender);

        userDeposit.amount -= amount;

        totalDeposits -= amount;

        arcd.safeTransfer(msg.sender, amount);
        emit Withdrawn(msg.sender, depositId, amount, uint8(userDeposit.lock));
    }

    /**
     * @notice Allows users to withdraw deposited tokens for a specific deposit
     *         deposit id. Lock period needs to have ended.
     *
     * @param depositId                        The specified deposit to exit.
     */
    function exit(uint256 depositId) external {
        withdraw(type(uint256).max, depositId);
    }

    /**
     * @notice Allows users to withdraw all their deposited tokens in one transaction.
     *         Lock period needs to have ended.
     */
    function exitAll() external whenNotPaused nonReentrant {
        UserDeposit[] storage userDeposits = deposits[msg.sender];
        uint256 totalWithdrawAmount = 0;
        uint256 totalVotingPower = 0;
        uint256 numUserDeposits = userDeposits.length;

        for (uint256 i = 0; i < numUserDeposits; ++i) {
            UserDeposit storage userDeposit = userDeposits[i];
            uint256 amount = userDeposit.amount;
            if (amount == 0 || block.timestamp < userDeposit.unlockTimestamp) continue;

            userDeposit.amount -= amount;

            totalVotingPower += amount;
            totalWithdrawAmount += amount;

            emit Withdrawn(msg.sender, i, amount, uint8(userDeposit.lock));
        }

        if (totalVotingPower > 0) {
            _subtractVotingPower(totalVotingPower, msg.sender);
        }

        if (totalWithdrawAmount > 0) {
            totalDeposits -= totalWithdrawAmount;
            arcd.safeTransfer(msg.sender, totalWithdrawAmount);
        }
    }

    // ======================================== RESTRICTED FUNCTIONS =========================================
    /**
     * @notice Allows the contract owner to recover ERC20 tokens locked in the contract.
     *         Deposited ARCD tokens cannot be recovered, they can only be withdrawn
     *         by the depositing user.
     *
     * @param tokenAddress                       The address of the token to recover.
     * @param tokenAmount                        The amount of token to recover.
     */
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
        if (tokenAddress == address(arcd)) revert ASS_DepositToken();
        if (tokenAddress == address(0)) revert ASS_ZeroAddress("token");
        if (tokenAmount == 0) revert ASS_ZeroAmount();

        IERC20(tokenAddress).safeTransfer(msg.sender, tokenAmount);

        emit Recovered(tokenAddress, tokenAmount);
    }

    /**
     * @notice Pauses the contract, callable by only the owner. Reversible.
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @notice Unpauses the contract, callable by only the owner. Reversible.
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    // ============================================== HELPERS ===============================================
    /**
     * @notice Internal deposit function updating a user's deposit balance information and voting
     *         power. The total supply of deposited tokens are updated accordingly.
     *
     * @param recipient                        The user whom the deposited tokens are allocated to.
     * @param amount                           The amount of tokens to deposit and lock.
     * @param delegation                       The address to which the user's voting power will be delegated.
     * @param lock                             The locking period for the deposited tokens.
     */
    function _deposit(
        address recipient,
        uint256 amount,
        address delegation,
        Lock lock
    ) internal nonReentrant whenNotPaused {
        if (amount == 0) revert ASS_ZeroAmount();
        if (delegation == address(0)) revert ASS_ZeroAddress("delegation");

        uint256 userDepositCount = deposits[recipient].length;
        if (userDepositCount >= MAX_DEPOSITS) revert ASS_DepositCountExceeded();

        uint256 lockDuration = _calculateLockDuration(lock);

        // update the vote power
        _addVotingPower(recipient, amount, delegation);

        // populate user deposit information
        deposits[recipient].push(
            UserDeposit({
                amount: amount,
                unlockTimestamp: uint32(block.timestamp + lockDuration),
                lock: lock
            })
        );

        totalDeposits += amount;

        arcd.safeTransferFrom(msg.sender, address(this), amount);

        emit Deposited(recipient, userDepositCount, amount, uint8(lock));
    }

    /**
     * @notice Calculates the lock duration for a user's deposit based on the selected lock SHORT, MEDIUM or LONG.
     *
     * @param lock                              The lock period committed.
     *
     * @return lockDuration                     The period duration for the selected lock.
     */
    function _calculateLockDuration(Lock lock) internal pure returns (uint256 lockDuration) {
        if (lock == Lock.Short) {
           lockDuration = SHORT_LOCK_TIME;
        } else if (lock == Lock.Medium) {
            lockDuration = MEDIUM_LOCK_TIME;
        } else if (lock == Lock.Long) {
            lockDuration = LONG_LOCK_TIME;
        }
    }

    /**
      * @notice This internal function is adapted from the external withdraw function in Council's
     *          LockingVault contract with 2 key modifications: it omits the token transfer transaction
     *          and adds an address account parameter to specify the user whose voting power needs updating.
     *          In the Locking Vault, msg.sender directly indicated the user, whereas in this context,
     *          msg.sender refers to the contract itself. Therefore, we explicitly pass the
     *          user's address.
     *
     * @param amount                           The amount of voting power to subtract.
     * @param account                          The account whose voting power to subtract.
     */
    function _subtractVotingPower(uint256 amount, address account) internal {
        if (amount > type(uint96).max) revert ASS_AmountTooBig();

        // Load our deposits storage
        Storage.AddressUint storage userData = _deposits()[account];

        // Reduce the user's stored balance
        userData.amount -= uint96(amount);
        address delegate = userData.who;

        // Reduce the delegate voting power
        // Get the storage pointer
        History.HistoricalBalances memory votingPower = _votingPower();
        // Load the most recent voter power stamp
        uint256 delegateeVotes = votingPower.loadTop(delegate);
        // remove the votes from the delegate
        votingPower.push(delegate, delegateeVotes - amount);
        // Emit an event to track votes
        emit VoteChange(account, delegate, -1 * int256(amount));
    }

    /**
     * @notice This internal function is adapted from the external deposit function in the Council
     *         LockingVault contract with 2 key modifications: it reverts if the specified delegation
     *         address does not match the user's previously designated delegate, and it no longer
     *         handles token transfers into the contract as these are handled by the deposit function.
     *
     * @param fundedAccount                    The address to credit the voting power to.
     * @param amount                           The amount of voting power to add.
     * @param delegation                       The user's delegatee address.
     */
    function _addVotingPower(
        address fundedAccount,
        uint256 amount,
        address delegation
    ) internal {
        if (amount > type(uint96).max) revert ASS_AmountTooBig();
        // No delegating to zero
        if (delegation == address(0)) revert ASS_ZeroAddress("delegation");

        // Load our deposits storage
        Storage.AddressUint storage userData = _deposits()[fundedAccount];
        // Load who has the user's votes
        address delegate = userData.who;

        if (delegate == address(0)) {
            // If the user is un-delegated we delegate to their indicated address
            delegate = delegation;
            // Set the delegation
            userData.who = delegate;
        } if (delegation != delegate) {
            revert ASS_InvalidDelegationAddress();
        }
        // Now we increase the user's balance
        userData.amount += uint96(amount);

        // Next we increase the delegation to their delegate
        // Get the storage pointer
        History.HistoricalBalances memory votingPower = _votingPower();
        // Load the most recent voter power stamp
        uint256 delegateeVotes = votingPower.loadTop(delegate);
        // Emit an event to track votes
        emit VoteChange(fundedAccount, delegate, int256(amount));
        // Add the newly deposited votes to the delegate
        votingPower.push(delegate, delegateeVotes + amount);
    }

    /**
     * @notice This function is taken from the Council LockingVault contract. It is a single
     *         endpoint for loading storage for deposits.
     *
     * @return                                  A storage mapping which can be used to look
     *                                          up deposit data.
     */
    function _deposits()
        internal
        pure
        returns (mapping(address => Storage.AddressUint) storage)
    {
        // This call returns a storage mapping with a unique non overwrite-able storage location
        // which can be persisted through upgrades, even if they change storage layout
        return (Storage.mappingAddressToPackedAddressUint("deposits"));
    }

    /**
     * @notice This function is taken from the Council LockingVault contract. Returns the
     *         historical voting power tracker.
     *
     *
     * @return                                  A struct which can push to and find items in
     *                                          block indexed storage.
     */
    function _votingPower()
        internal
        pure
        returns (History.HistoricalBalances memory)
    {
        // This call returns a storage mapping with a unique non overwrite-able storage location
        // which can be persisted through upgrades, even if they change storage layout
        return (History.load("votingPower"));
    }

    /**
     * @notice This function is taken from the Council LockingVault contract. Loads the voting
     *         power of a user. It is revised to no longer clear stale blocks from the queue
     *         in order to avoid gas depletion encountered with overly long queues.
     *
     * @param user                              The address we want to load the voting power of.
     * @param blockNumber                       The block number we want the user's voting power at.
     *
     * @return                                  The number of votes.
     */
    function queryVotePower(
        address user,
        uint256 blockNumber,
        bytes calldata
    ) external view override returns (uint256) {
        return queryVotePowerView(user, blockNumber);
    }

    /**
     * @notice This function is taken from the LockingVault contract. Loads the voting power of a
     *         user without any changes to state.
     *
     * @param user                              The address we want to load the voting power of.
     * @param blockNumber                       The block number we want the user's voting power at.
     *
     * @return                                  The number of votes.
     */
    function queryVotePowerView(address user, uint256 blockNumber)
        public
        view
        returns (uint256)
    {
        // Get our reference to historical data
        History.HistoricalBalances memory votingPower = _votingPower();
        // Find the historical datum
        return votingPower.find(user, blockNumber);
    }

    /**
     * @notice This function is taken from the Council LockingVault contract, it changes a user's
     *         voting power delegatee.
     *
     * @param newDelegate                        The new address which gets the voting power.
     */
    function changeDelegation(address newDelegate) external {
        // No delegating to zero
        if (newDelegate == address(0)) revert ASS_ZeroAddress("delegation");
        // Get the stored user data
        Storage.AddressUint storage userData = _deposits()[msg.sender];
        // Get the user balance
        uint256 userBalance = uint256(userData.amount);
        address oldDelegate = userData.who;
        // Reset the user delegation
        userData.who = newDelegate;
        // Reduce the old voting power
        // Get the storage pointer
        History.HistoricalBalances memory votingPower = _votingPower();
        // Load the old delegate's voting power
        uint256 oldDelegateVotes = votingPower.loadTop(oldDelegate);
        // Reduce the old voting power
        votingPower.push(oldDelegate, oldDelegateVotes - userBalance);
        // Emit an event to track votes
        emit VoteChange(msg.sender, oldDelegate, -1 * int256(userBalance));
        // Get the new delegate's votes
        uint256 newDelegateVotes = votingPower.loadTop(newDelegate);
        // Store the increase in power
        votingPower.push(newDelegate, newDelegateVotes + userBalance);
        // Emit an event tracking this voting power change
        emit VoteChange(msg.sender, newDelegate, int256(userBalance));
    }
}

File 4 of 19 : SingleSidedStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title SingleSidedStaking
 * @author Non-Fungible Technologies, Inc.
 *
 * This file contains all custom errors for the ArcadeSingleSidedStaking contract.
 * All errors are prefixed by  "ASS_" for ArcadeSingleSidedStaking. Errors located in one place
 * to make it possible to holistically look at all the failure cases.
 */

// ==================================== Arcade Single Sided Staking Errors ======================================

/**
 * @notice Zero address passed in where not allowed.
 * @param addressType                The name of the parameter for which a zero
 *                                   address was provided.
 */
error ASS_ZeroAddress(string addressType);

/**
 * @notice Cannot withdraw or stake amount zero.
 */
error ASS_ZeroAmount();

/**
 * @notice Deposit token cannot be ERC20 recovered.
 */
error ASS_DepositToken();

/**
 * @notice User tries to withdraw an amount greater than
 *         than their balance.
 */
error ASS_BalanceAmount();

/**
 * @notice Cannot withdraw a deposit which is still locked.
 */
error ASS_Locked();

/**
 * @notice Deposits number is larger than MAX_ITERATIONS.
 *
 */
error ASS_DepositCountExceeded();

/**
 * @notice The provided delegate address does not match their initial delegate.
 */
 error ASS_InvalidDelegationAddress();

/**
 * @notice Amount cannot exceed the maximum value that can be held by a uint96.
 */
 error ASS_AmountTooBig();

// ==================================== Airdrop Single Sided Staking Errors ======================================

 /**
 * @notice The caller is not authorized to make this call.
 */
 error ASS_CallerNotAirdropDistribution();

File 5 of 19 : IArcadeSingleSidedStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface IArcadeSingleSidedStaking {
    // ================================================= EVENTS ==================================================
    event Deposited(address indexed user, uint256 depositId, uint256 amount, uint8 lock);
    event Withdrawn(address indexed user, uint256 depositId, uint256 amount, uint8 lock);
    event Recovered(address token, uint256 amount);
    event VoteChange(address indexed from, address indexed to, int256 amount);

    // ================================================= STRUCTS =================================================
    enum Lock {
        Short,
        Medium,
        Long
    }

    struct UserDeposit {
        Lock lock;
        uint32 unlockTimestamp;
        uint256 amount;
    }

    // ============================================= VIEW FUNCTIONS ==============================================
    function getTotalUserDeposits(address account) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function getActiveDeposits(address account) external view returns (uint256[] memory);

    function getLastDepositId(address account) external view returns (uint256);

    function getUserDeposit(address account, uint256 depositId) external view returns (uint8 lock, uint32 unlockTimestamp, uint256 amount);

    function balanceOfDeposit(address account, uint256 depositId) external view returns (uint256);

    // =========================================== MUTATIVE FUNCTIONS ============================================
    function exitAll() external;

    function exit(uint256 depositId) external;

    function deposit(uint256 amount, address firstDelegation, Lock lock) external;

    function withdraw(uint256 amount, uint256 depositId) external;

    function recoverERC20(address tokenAddress, uint256 tokenAmount) external;

    function pause() external;

    function unpause() external;
}

File 6 of 19 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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 Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 7 of 19 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 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;
    }
}

File 8 of 19 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 9 of 19 : Ownable.sol
// 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);
    }
}

File 10 of 19 : IVotingVault.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

interface IVotingVault {
    /// @notice Attempts to load the voting power of a user
    /// @param user The address we want to load the voting power of
    /// @param blockNumber the block number we want the user's voting power at
    /// @param extraData Abi encoded optional extra data used by some vaults, such as merkle proofs
    /// @return the number of votes
    function queryVotePower(
        address user,
        uint256 blockNumber,
        bytes calldata extraData
    ) external returns (uint256);
}

File 11 of 19 : History.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

import "./Storage.sol";

// This library is an assembly optimized storage library which is designed
// to track timestamp history in a struct which uses hash derived pointers.
// WARNING - Developers using it should not access the underlying storage
// directly since we break some assumptions of high level solidity. Please
// note this library also increases the risk profile of memory manipulation
// please be cautious in your usage of uninitialized memory structs and other
// anti patterns.
library History {
    // The storage layout of the historical array looks like this
    // [(128 bit min index)(128 bit length)] [0][0] ... [(64 bit block num)(192 bit data)] .... [(64 bit block num)(192 bit data)]
    // We give the option to the invoker of the search function the ability to clear
    // stale storage. To find data we binary search for the block number we need
    // This library expects the blocknumber indexed data to be pushed in ascending block number
    // order and if data is pushed with the same blocknumber it only retains the most recent.
    // This ensures each blocknumber is unique and contains the most recent data at the end
    // of whatever block it indexes [as long as that block is not the current one].

    // A struct which wraps a memory pointer to a string and the pointer to storage
    // derived from that name string by the storage library
    // WARNING - For security purposes never directly construct this object always use load
    struct HistoricalBalances {
        string name;
        // Note - We use bytes32 to reduce how easy this is to manipulate in high level sol
        bytes32 cachedPointer;
    }

    /// @notice The method by which inheriting contracts init the HistoricalBalances struct
    /// @param name The name of the variable. Note - these are globals, any invocations of this
    ///             with the same name work on the same storage.
    /// @return The memory pointer to the wrapper of the storage pointer
    function load(string memory name)
        internal
        pure
        returns (HistoricalBalances memory)
    {
        mapping(address => uint256[]) storage storageData =
            Storage.mappingAddressToUnit256ArrayPtr(name);
        bytes32 pointer;
        assembly {
            pointer := storageData.slot
        }
        return HistoricalBalances(name, pointer);
    }

    /// @notice An unsafe method of attaching the cached ptr in a historical balance memory objects
    /// @param pointer cached pointer to storage
    /// @return storageData A storage array mapping pointer
    /// @dev PLEASE DO NOT USE THIS METHOD WITHOUT SERIOUS REVIEW. IF AN EXTERNAL ACTOR CAN CALL THIS WITH
    //       ARBITRARY DATA THEY MAY BE ABLE TO OVERWRITE ANY STORAGE IN THE CONTRACT.
    function _getMapping(bytes32 pointer)
        private
        pure
        returns (mapping(address => uint256[]) storage storageData)
    {
        assembly {
            storageData.slot := pointer
        }
    }

    /// @notice This function adds a block stamp indexed piece of data to a historical data array
    ///         To prevent duplicate entries if the top of the array has the same blocknumber
    ///         the value is updated instead
    /// @param wrapper The wrapper which hold the reference to the historical data storage pointer
    /// @param who The address which indexes the array we need to push to
    /// @param data The data to append, should be at most 192 bits and will revert if not
    function push(
        HistoricalBalances memory wrapper,
        address who,
        uint256 data
    ) internal {
        // Check preconditions
        // OoB = Out of Bounds, short for contract bytecode size reduction
        require(data <= type(uint192).max, "OoB");
        // Get the storage this is referencing
        mapping(address => uint256[]) storage storageMapping =
            _getMapping(wrapper.cachedPointer);
        // Get the array we need to push to
        uint256[] storage storageData = storageMapping[who];
        // We load the block number and then shift it to be in the top 64 bits
        uint256 blockNumber = block.number << 192;
        // We combine it with the data, because of our require this will have a clean
        // top 64 bits
        uint256 packedData = blockNumber | data;
        // Load the array length
        (uint256 minIndex, uint256 length) = _loadBounds(storageData);
        // On the first push we don't try to load
        uint256 loadedBlockNumber = 0;
        if (length != 0) {
            (loadedBlockNumber, ) = _loadAndUnpack(storageData, length - 1);
        }
        // The index we push to, note - we use this pattern to not branch the assembly
        uint256 index = length;
        // If the caller is changing data in the same block we change the entry for this block
        // instead of adding a new one. This ensures each block numb is unique in the array.
        if (loadedBlockNumber == block.number) {
            index = length - 1;
        }
        // We use assembly to write our data to the index
        assembly {
            // Stores packed data in the equivalent of storageData[length]
            sstore(
                add(
                    // The start of the data slots
                    add(storageData.slot, 1),
                    // index where we store
                    index
                ),
                packedData
            )
        }
        // Reset the boundaries if they changed
        if (loadedBlockNumber != block.number) {
            _setBounds(storageData, minIndex, length + 1);
        }
    }

    /// @notice Loads the most recent timestamp of delegation power
    /// @param wrapper The memory struct which we want to search for historical data
    /// @param who The user who's balance we want to load
    /// @return the top slot of the array
    function loadTop(HistoricalBalances memory wrapper, address who)
        internal
        view
        returns (uint256)
    {
        // Load the storage pointer
        uint256[] storage userData = _getMapping(wrapper.cachedPointer)[who];
        // Load the length
        (, uint256 length) = _loadBounds(userData);
        // If it's zero no data has ever been pushed so we return zero
        if (length == 0) {
            return 0;
        }
        // Load the current top
        (, uint256 storedData) = _loadAndUnpack(userData, length - 1);
        // and return it
        return (storedData);
    }

    /// @notice Finds the data stored with the highest block number which is less than or equal to a provided
    ///         blocknumber.
    /// @param wrapper The memory struct which we want to search for historical data
    /// @param who The address which indexes the array to be searched
    /// @param blocknumber The blocknumber we want to load the historical data of
    /// @return The loaded unpacked data at this point in time.
    function find(
        HistoricalBalances memory wrapper,
        address who,
        uint256 blocknumber
    ) internal view returns (uint256) {
        // Get the storage this is referencing
        mapping(address => uint256[]) storage storageMapping =
            _getMapping(wrapper.cachedPointer);
        // Get the array we need to push to
        uint256[] storage storageData = storageMapping[who];
        // Pre load the bounds
        (uint256 minIndex, uint256 length) = _loadBounds(storageData);
        // Search for the blocknumber
        (, uint256 loadedData) =
            _find(storageData, blocknumber, 0, minIndex, length);
        // In this function we don't have to change the stored length data
        return (loadedData);
    }

    /// @notice Finds the data stored with the highest blocknumber which is less than or equal to a provided block number
    ///         Opportunistically clears any data older than staleBlock which is possible to clear.
    /// @param wrapper The memory struct which points to the storage we want to search
    /// @param who The address which indexes the historical data we want to search
    /// @param blocknumber The blocknumber we want to load the historical state of
    /// @param staleBlock A block number which we can [but are not obligated to] delete history older than
    /// @return The found data
    function findAndClear(
        HistoricalBalances memory wrapper,
        address who,
        uint256 blocknumber,
        uint256 staleBlock
    ) internal returns (uint256) {
        // Get the storage this is referencing
        mapping(address => uint256[]) storage storageMapping =
            _getMapping(wrapper.cachedPointer);
        // Get the array we need to push to
        uint256[] storage storageData = storageMapping[who];
        // Pre load the bounds
        (uint256 minIndex, uint256 length) = _loadBounds(storageData);
        // Search for the blocknumber
        (uint256 staleIndex, uint256 loadedData) =
            _find(storageData, blocknumber, staleBlock, minIndex, length);
        // We clear any data in the stale region
        // Note - Since find returns 0 if no stale data is found and we use > instead of >=
        //        this won't trigger if no stale data is found. Plus it won't trigger on minIndex == staleIndex
        //        == maxIndex and clear the whole array.
        if (staleIndex > minIndex) {
            // Delete the outdated stored info
            _clear(minIndex, staleIndex, storageData);
            // Reset the array info with stale index as the new minIndex
            _setBounds(storageData, staleIndex, length);
        }
        return (loadedData);
    }

    /// @notice Searches for the data stored at the largest blocknumber index less than a provided parameter.
    ///         Allows specification of a expiration stamp and returns the greatest examined index which is
    ///         found to be older than that stamp.
    /// @param data The stored data
    /// @param blocknumber the blocknumber we want to load the historical data for.
    /// @param staleBlock The oldest block that we care about the data stored for, all previous data can be deleted
    /// @param startingMinIndex The smallest filled index in the array
    /// @param length the length of the array
    /// @return Returns the largest stale data index seen or 0 for no seen stale data and the stored data
    function _find(
        uint256[] storage data,
        uint256 blocknumber,
        uint256 staleBlock,
        uint256 startingMinIndex,
        uint256 length
    ) private view returns (uint256, uint256) {
        // We explicitly revert on the reading of memory which is uninitialized
        require(length != 0, "uninitialized");
        // Do some correctness checks
        require(staleBlock <= blocknumber);
        require(startingMinIndex < length);
        // Load the bounds of our binary search
        uint256 maxIndex = length - 1;
        uint256 minIndex = startingMinIndex;
        uint256 staleIndex = 0;

        // We run a binary search on the block number fields in the array between
        // the minIndex and maxIndex. If we find indexes with blocknumber < staleBlock
        // we set staleIndex to them and return that data for an optional clearing step
        // in the calling function.
        while (minIndex != maxIndex) {
            // We use the ceil instead of the floor because this guarantees that
            // we pick the highest blocknumber less than or equal the requested one
            uint256 mid = (minIndex + maxIndex + 1) / 2;
            // Load and unpack the data in the midpoint index
            (uint256 pastBlock, uint256 loadedData) = _loadAndUnpack(data, mid);

            //  If we've found the exact block we are looking for
            if (pastBlock == blocknumber) {
                // Then we just return the data
                return (staleIndex, loadedData);

                // Otherwise if the loaded block is smaller than the block number
            } else if (pastBlock < blocknumber) {
                // Then we first check if this is possibly a stale block
                if (pastBlock < staleBlock) {
                    // If it is we mark it for clearing
                    staleIndex = mid;
                }
                // We then repeat the search logic on the indices greater than the midpoint
                minIndex = mid;

                // In this case the pastBlock > blocknumber
            } else {
                // We then repeat the search on the indices below the midpoint
                maxIndex = mid - 1;
            }
        }

        // We load at the final index of the search
        (uint256 _pastBlock, uint256 _loadedData) =
            _loadAndUnpack(data, minIndex);
        // This will only be hit if a user has misconfigured the stale index and then
        // tried to load father into the past than has been preserved
        require(_pastBlock <= blocknumber, "Search Failure");
        return (staleIndex, _loadedData);
    }

    /// @notice Clears storage between two bounds in array
    /// @param oldMin The first index to set to zero
    /// @param newMin The new minimum filled index, ie clears to index < newMin
    /// @param data The storage array pointer
    function _clear(
        uint256 oldMin,
        uint256 newMin,
        uint256[] storage data
    ) private {
        // Correctness checks on this call
        require(oldMin <= newMin);
        // This function is private and trusted and should be only called by functions which ensure
        // that oldMin < newMin < length
        assembly {
            // The layout of arrays in solidity is [length][data]....[data] so this pointer is the
            // slot to write to data
            let dataLocation := add(data.slot, 1)
            // Loop through each index which is below new min and clear the storage
            // Note - Uses strict min so if given an input like oldMin = 5 newMin = 5 will be a no op
            for {
                let i := oldMin
            } lt(i, newMin) {
                i := add(i, 1)
            } {
                // store at the starting data pointer + i 256 bits of zero
                sstore(add(dataLocation, i), 0)
            }
        }
    }

    /// @notice Loads and unpacks the block number index and stored data from a data array
    /// @param data the storage array
    /// @param i the index to load and unpack
    /// @return (block number, stored data)
    function _loadAndUnpack(uint256[] storage data, uint256 i)
        private
        view
        returns (uint256, uint256)
    {
        // This function is trusted and should only be called after checking data lengths
        // we use assembly for the sload to avoid reloading length.
        uint256 loaded;
        assembly {
            loaded := sload(add(add(data.slot, 1), i))
        }
        // Unpack the packed 64 bit block number and 192 bit data field
        return (
            loaded >> 192,
            loaded &
                0x0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff
        );
    }

    /// @notice This function sets our non standard bounds data field where a normal array
    ///         would have length
    /// @param data the pointer to the storage array
    /// @param minIndex The minimum non stale index
    /// @param length The length of the storage array
    function _setBounds(
        uint256[] storage data,
        uint256 minIndex,
        uint256 length
    ) private {
        // Correctness check
        require(minIndex < length);

        assembly {
            // Ensure data cleanliness
            let clearedLength := and(
                length,
                0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff
            )
            // We move the min index into the top 128 bits by shifting it left by 128 bits
            let minInd := shl(128, minIndex)
            // We pack the data using binary or
            let packed := or(minInd, clearedLength)
            // We store in the packed data in the length field of this storage array
            sstore(data.slot, packed)
        }
    }

    /// @notice This function loads and unpacks our packed min index and length for our custom storage array
    /// @param data The pointer to the storage location
    /// @return minInd the first filled index in the array
    /// @return length the length of the array
    function _loadBounds(uint256[] storage data)
        private
        view
        returns (uint256 minInd, uint256 length)
    {
        // Use assembly to manually load the length storage field
        uint256 packedData;
        assembly {
            packedData := sload(data.slot)
        }
        // We use a shift right to clear out the low order bits of the data field
        minInd = packedData >> 128;
        // We use a binary and to extract only the bottom 128 bits
        length =
            packedData &
            0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
    }
}

File 12 of 19 : Storage.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

// This library allows for secure storage pointers across proxy implementations
// It will return storage pointers based on a hashed name and type string.
library Storage {
    // This library follows a pattern which if solidity had higher level
    // type or macro support would condense quite a bit.

    // Each basic type which does not support storage locations is encoded as
    // a struct of the same name capitalized and has functions 'load' and 'set'
    // which load the data and set the data respectively.

    // All types will have a function of the form 'typename'Ptr('name') -> storage ptr
    // which will return a storage version of the type with slot which is the hash of
    // the variable name and type string. This pointer allows easy state management between
    // upgrades and overrides the default solidity storage slot system.

    /// @dev The address type container
    struct Address {
        address data;
    }

    /// @notice A function which turns a variable name for a storage address into a storage
    ///         pointer for its container.
    /// @param name the variable name
    /// @return data the storage pointer
    function addressPtr(string memory name)
        internal
        pure
        returns (Address storage data)
    {
        bytes32 typehash = keccak256("address");
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        assembly {
            data.slot := offset
        }
    }

    /// @notice A function to load an address from the container struct
    /// @param input the storage pointer for the container
    /// @return the loaded address
    function load(Address storage input) internal view returns (address) {
        return input.data;
    }

    /// @notice A function to set the internal field of an address container
    /// @param input the storage pointer to the container
    /// @param to the address to set the container to
    function set(Address storage input, address to) internal {
        input.data = to;
    }

    /// @dev The uint256 type container
    struct Uint256 {
        uint256 data;
    }

    /// @notice A function which turns a variable name for a storage uint256 into a storage
    ///         pointer for its container.
    /// @param name the variable name
    /// @return data the storage pointer
    function uint256Ptr(string memory name)
        internal
        pure
        returns (Uint256 storage data)
    {
        bytes32 typehash = keccak256("uint256");
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        assembly {
            data.slot := offset
        }
    }

    /// @notice A function to load an uint256 from the container struct
    /// @param input the storage pointer for the container
    /// @return the loaded uint256
    function load(Uint256 storage input) internal view returns (uint256) {
        return input.data;
    }

    /// @notice A function to set the internal field of a unit256 container
    /// @param input the storage pointer to the container
    /// @param to the address to set the container to
    function set(Uint256 storage input, uint256 to) internal {
        input.data = to;
    }

    /// @notice Returns the storage pointer for a named mapping of address to uint256
    /// @param name the variable name for the pointer
    /// @return data the mapping pointer
    function mappingAddressToUnit256Ptr(string memory name)
        internal
        pure
        returns (mapping(address => uint256) storage data)
    {
        bytes32 typehash = keccak256("mapping(address => uint256)");
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        assembly {
            data.slot := offset
        }
    }

    /// @notice Returns the storage pointer for a named mapping of address to uint256[]
    /// @param name the variable name for the pointer
    /// @return data the mapping pointer
    function mappingAddressToUnit256ArrayPtr(string memory name)
        internal
        pure
        returns (mapping(address => uint256[]) storage data)
    {
        bytes32 typehash = keccak256("mapping(address => uint256[])");
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        assembly {
            data.slot := offset
        }
    }

    /// @notice Allows external users to calculate the slot given by this lib
    /// @param typeString the string which encodes the type
    /// @param name the variable name
    /// @return the slot assigned by this lib
    function getPtr(string memory typeString, string memory name)
        external
        pure
        returns (uint256)
    {
        bytes32 typehash = keccak256(abi.encodePacked(typeString));
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        return (uint256)(offset);
    }

    // A struct which represents 1 packed storage location with a compressed
    // address and uint96 pair
    struct AddressUint {
        address who;
        uint96 amount;
    }

    /// @notice Returns the storage pointer for a named mapping of address to uint256[]
    /// @param name the variable name for the pointer
    /// @return data the mapping pointer
    function mappingAddressToPackedAddressUint(string memory name)
        internal
        pure
        returns (mapping(address => AddressUint) storage data)
    {
        bytes32 typehash = keccak256("mapping(address => AddressUint)");
        bytes32 offset = keccak256(abi.encodePacked(typehash, name));
        assembly {
            data.slot := offset
        }
    }
}

File 13 of 19 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

File 14 of 19 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

    /**
     * @dev Returns the 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);
}

File 15 of 19 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 16 of 19 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 17 of 19 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (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;
    }
}

File 18 of 19 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 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 19 of 19 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_arcd","type":"address"},{"internalType":"address","name":"_airdropDistribution","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ASS_AmountTooBig","type":"error"},{"inputs":[],"name":"ASS_BalanceAmount","type":"error"},{"inputs":[],"name":"ASS_CallerNotAirdropDistribution","type":"error"},{"inputs":[],"name":"ASS_DepositCountExceeded","type":"error"},{"inputs":[],"name":"ASS_DepositToken","type":"error"},{"inputs":[],"name":"ASS_InvalidDelegationAddress","type":"error"},{"inputs":[],"name":"ASS_Locked","type":"error"},{"inputs":[{"internalType":"string","name":"addressType","type":"string"}],"name":"ASS_ZeroAddress","type":"error"},{"inputs":[],"name":"ASS_ZeroAmount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"airdropDistribution","type":"address"}],"name":"AirdropDistributionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"lock","type":"uint8"}],"name":"Deposited","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"VoteChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"lock","type":"uint8"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"LONG_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DEPOSITS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MEDIUM_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHORT_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"airdropDistribution","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"delegation","type":"address"},{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"}],"name":"airdropReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"arcd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"balanceOfDeposit","outputs":[{"internalType":"uint256","name":"depositBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newDelegate","type":"address"}],"name":"changeDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"delegation","type":"address"},{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"deposits","outputs":[{"internalType":"enum IArcadeSingleSidedStaking.Lock","name":"lock","type":"uint8"},{"internalType":"uint32","name":"unlockTimestamp","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exitAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getActiveDeposits","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getLastDepositId","outputs":[{"internalType":"uint256","name":"lastDepositId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTotalUserDeposits","outputs":[{"internalType":"uint256","name":"userBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"getUserDeposit","outputs":[{"internalType":"uint8","name":"lock","type":"uint8"},{"internalType":"uint32","name":"unlockTimestamp","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"queryVotePower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"queryVotePowerView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_airdropDistribution","type":"address"}],"name":"setAirdropDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","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":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a034620001ab57601f62001f9738819003918201601f191683019291906001600160401b03841183851017620001b0578160609284926040968752833981010312620001ab576200005181620001c6565b906200006d836200006560208401620001c6565b9201620001c6565b60016000556001600160a01b039290919083169081156200019357839060015492865193818482167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a36001600160a81b03191617600155169081156200016957506080521680156200012457600480546001600160a01b03191691909117905551611dbb9081620001dc823960805181818161076d015281816109f101528181610efa0152818161112601526113b90152f35b8151635a8af66b60e01b815260206004820152601360248201527f61697264726f70446973747269627574696f6e000000000000000000000000006044820152606490fd5b606490635a8af66b60e01b8152602060048201526004602482015263185c98d960e21b6044820152fd5b8451631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001ab5756fe6080604081815260048036101561001557600080fd5b600092833560e01c9081630f0cb0b9146114245750806318160ddd14610f865780632cbb43fc146114065780632d984723146113e8578063375c22cd146113a45780633f4ba83a14611335578063441a3e701461128d578063506384171461121b5780635b6c508c146110ba5780635c975abb14611093578063600381f0146110755780636071105814610ff3578063715018a614610fa55780637d88209714610f865780637f8661a114610e6257806381a4fdd814610e1a5780638456cb5914610db957806387cef13014610aae5780638980f11f146109bb5780638da5cb5b146109925780639f973fd5146108ab578063b7ec8d4b1461054e578063d2d48ee3146103f0578063d6d681771461035f578063de1b905214610343578063deac03011461031c578063e7d20283146102f2578063e91f323514610253578063ea32f42e146101e75763f2fde38b1461016d57600080fd5b346101e35760203660031901126101e35761018661147c565b9061018f6114c9565b6001600160a01b039182169283156101cd575050600180546001600160a01b03198116841790915516600080516020611d668339815191528380a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b83823461024f57602036600319011261024f5761020261147c565b61020a6114c9565b81546001600160a01b0319166001600160a01b03919091169081179091557fc35d72a054acf727dbcb414ab4b300b86c00481ccf567ab027bd57a0fb7727828280a280f35b5080fd5b50346101e35760603660031901126101e35761026d61147c565b90604435906001600160401b03908183116102ee57366023840112156102ee578201359081116102ea57369101602401116101e357816020936102e292856102b361190a565b01516001600160a01b03909116825285522080546001600160801b0381169160809190911c9060243590611b4c565b915191825250f35b8480fd5b8580fd5b50503461024f578060031936011261024f576102e28160209361031361147c565b856102b361190a565b50346101e357826003193601126101e3575490516001600160a01b03909116815260209150f35b50503461024f578160031936011261024f576020905160148152f35b5091903461024f578060031936011261024f5761037a61147c565b6001600160a01b031682526002602052808220805460243591908210156103ec57906103a591611497565b50908154600160ff82169301549282519460038210156103d95750845260081c63ffffffff16602084015282015260609150f35b634e487b7160e01b815260218752602490fd5b8380fd5b50346101e3576020806003193601126103ec57926001600160a01b0361041461147c565b168152600284528281209181908354835b81811061050e575061044e610439846115be565b936104468851958661159b565b8085526115be565b8388019590601f19013687378490855b8381106104a557505050505083519485948186019282875251809352850193925b82811061048e57505050500390f35b83518552869550938101939281019260010161047f565b60016104b782849c99989a9b9c611497565b5001546104d3575b6104c8906114f5565b98979694959861045e565b9187518110156104fb576104f38184896104c89460051b8c0101526114f5565b9290506104bf565b634e487b7160e01b865260328552602486fd5b600161051e82889a979698611497565b500154610539575b61052f906114f5565b9694929396610425565b9461054661052f916114f5565b959050610526565b5091903461024f57606036600319011261024f5760249283356001600160a01b038181169383359290918582036108a7576044359460038610156108a35761059461179e565b61059c61177d565b84156108955786156108775733885260209260028452848920549760148910156108675789918289610839575062278d00945b6001600160601b0390818a11610829576105ef6105ea6118cc565b611c8e565b8d3390528852888d2090815485828216958615610810575b505050831680940361080057908c969594939291818b16815460a01c019182116107ee5761067593929161063a916117e6565b61066f8a61064661190a565b936106518486611afd565b908c51838152600080516020611d268339815191528d3392a361151a565b916119c7565b3384526002855263ffffffff61068e878620944261151a565b169086519061069c82611534565b6107dc5788815285810191825286810188815284549094600160401b8210156107ca57906106cf91600182018155611497565b9590956107b95750519160038310156107a7575092600161079d969593600080516020611d468339815191529a9b9c936107919664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff1916171783555191015561073b8660035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528460648201526064815261076b81611580565b7f0000000000000000000000000000000000000000000000000000000000000000611647565b519283923396846115d5565b0390a26001815580f35b634e487b7160e01b8b52602190528a8afd5b634e487b7160e01b81528084528c90fd5b634e487b7160e01b8752604185528d87fd5b634e487b7160e01b8552602183528b85fd5b634e487b7160e01b8852601186528e88fd5b885163be6422b760e01b81528690fd5b6001600160a01b03199092161783559350388581610607565b885163034c196960e31b81528690fd5b8b935060018a0361084f5750624f1a00946105cf565b948b93506001198a016105cf5762c5c10095506105cf565b85516308f0bb4160e21b81528390fd5b8351635a8af66b60e01b81529081906108919082016117c1565b0390fd5b8351633e9842ab60e01b8152fd5b8780fd5b8680fd5b5090346101e35760203660031901126101e3576108c661147c565b6001600160a01b0381811693909290841561097857506108e76105ea6118cc565b33865260205280852080546001600160a01b03198116861790915560a081901c93169161096c9061091661190a565b9061093461092d876109288886611afd565b611527565b86846119c7565b61093d86611809565b948451958652600080516020611d26833981519152958660203392a361066f866109678385611afd565b61151a565b5191825260203392a380f35b9051635a8af66b60e01b81529081906108919082016117c1565b50503461024f578160031936011261024f5760015490516001600160a01b039091168152602090f35b5082903461024f578260031936011261024f576109d661147c565b602435916109e26114c9565b6001600160a01b0382811691907f0000000000000000000000000000000000000000000000000000000000000000168214610aa0578115610a74578315610a6657507f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289394610a5684610a6093339061160b565b51928392836115f0565b0390a180f35b8551633e9842ab60e01b8152fd5b6064906020875191635a8af66b60e01b835282015260056024820152643a37b5b2b760d91b6044820152fd5b85516305c6159560e31b8152fd5b5082903461024f57608036600319011261024f57610aca61147c565b602493843591604435916001600160a01b03808416908185036108a357606435936003851015610db557818854163303610da557610b0661179e565b610b0e61177d565b8615610d95578215610d7d5781169687895260209260028452848a2054966014881015610867578a918288610d4f575062278d00945b6001600160601b0390818c1161082957888e8e610b626105ea6118cc565b9082528a5220908154818116958615610d36575b5050841680930361080057908d969594939291818d16815460a01c01918211610d24578c8a8f94610bae610bdc979561066f956117e6565b610bb661190a565b95600080516020611d268339815191528d610bd1888a611afd565b9451868152a361151a565b8984526002855263ffffffff610bf5878620944261151a565b1690865190610c0382611534565b610d12578781528581019182528681018a815284549094600160401b821015610d005790610c3691600182018155611497565b959095610cef575051916003831015610cdd575092600161079d97969593600080516020611d468339815191529b9c9d93610cd39664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff19161717835551910155610ca38860035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528660648201526064815261076b81611580565b51938493846115d5565b634e487b7160e01b8c52602190528b8bfd5b634e487b7160e01b81528084528d90fd5b634e487b7160e01b8752604185528e87fd5b634e487b7160e01b8552602183528c85fd5b634e487b7160e01b8852601186528f88fd5b6001600160a01b03199091168517835594503880610b76565b8c935060018903610d655750624f1a0094610b44565b948c93506001198901610b445762c5c1009550610b44565b8351635a8af66b60e01b815280610891818b016117c1565b8351633e9842ab60e01b81528890fd5b83516303b22c3f60e51b81528890fd5b8880fd5b50503461024f578160031936011261024f5760207f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25891610df76114c9565b610dff61177d565b6001805460ff60a01b1916600160a01b17905551338152a180f35b50503461024f578060031936011261024f57602091600190610e58906001600160a01b03610e4661147c565b16815260028552836024359120611497565b5001549051908152f35b5091903461024f57602036600319011261024f57823590610e8161177d565b610e8961179e565b6000193384526002602052610ea083838620611497565b5090600182019182548015610f765763ffffffff825460081c164210610f6757808311610f5e575b5060ff9192610ed7338561182a565b610ee2848254611527565b9055610ef083600354611527565b600355610f1e83337f000000000000000000000000000000000000000000000000000000000000000061160b565b5416916003831015610f4b579061079d600080516020611d068339815191529392519283923396846115d5565b634e487b7160e01b855260218652602485fd5b915060ff610ec8565b845162b4ffe560e41b81528890fd5b845163460e84d760e11b81528890fd5b50503461024f578160031936011261024f576020906003549051908152f35b8334610ff05780600319360112610ff057610fbe6114c9565b600180546001600160a01b0319811690915581906001600160a01b0316600080516020611d668339815191528280a380f35b80fd5b5091903461024f578060031936011261024f576001600160a01b0361101661147c565b168252600260205261102c602435828420611497565b509081549160ff8316936003851015611062575060609450600163ffffffff91015492825194855260081c166020840152820152f35b634e487b7160e01b815260218652602490fd5b50503461024f578160031936011261024f5760209051624f1a008152f35b50503461024f578160031936011261024f5760209060ff60015460a01c1690519015158152f35b5090346101e357826003193601126101e357906110d561177d565b6110dd61179e565b338352600260205281832080548493849392845b8381106111615787868880611150575b5080611110575b506001815580f35b8061112061114a92600354611527565b600355337f000000000000000000000000000000000000000000000000000000000000000061160b565b81611108565b61115b90339061182a565b82611101565b61116b8183611497565b5096600188019081549182158015611208575b6111fa578261119f8160ff946111a5946111988380611527565b905561151a565b9961151a565b98541660038110156111e75790600080516020611d068339815191526111d76111df94938751918291339587846115d5565b0390a26114f5565b9594956110f1565b634e487b7160e01b8a526021875260248afd5b50969750506111df906114f5565b5063ffffffff8a5460081c16421061117e565b50503461024f57602036600319011261024f5761123661147c565b6001600160a01b03168252600260205280822080549092919082905b808210611263576020848451908152f35b90926112816112879160016112788789611497565b5001549061151a565b936114f5565b90611252565b5091903461024f578060031936011261024f576024359083356112ae61177d565b6112b661179e565b8081156113255733855260026020526112d184848720611497565b50600181019283549081156113155763ffffffff835460081c164210611306578110610f5e575060ff9192610ed7338561182a565b855162b4ffe560e41b81528990fd5b855163460e84d760e11b81528990fd5b8251633e9842ab60e01b81528690fd5b50346101e357826003193601126101e35761134e6114c9565b6001549060ff8260a01c1615611396575060ff60a01b1916600155513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a180f35b8251638dfc202b60e01b8152fd5b50503461024f578160031936011261024f57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50503461024f578160031936011261024f576020905162278d008152f35b50503461024f578160031936011261024f576020905162c5c1008152f35b905083833461024f57602036600319011261024f576001600160a01b0361144961147c565b168252600260205281205460001981019190821161146957602083838152f35b634e487b7160e01b815260118452602490fd5b600435906001600160a01b038216820361149257565b600080fd5b80548210156114b35760005260206000209060011b0190600090565b634e487b7160e01b600052603260045260246000fd5b6001546001600160a01b031633036114dd57565b60405163118cdaa760e01b8152336004820152602490fd5b60001981146115045760010190565b634e487b7160e01b600052601160045260246000fd5b9190820180921161150457565b9190820391821161150457565b606081019081106001600160401b0382111761154f57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761154f57604052565b60a081019081106001600160401b0382111761154f57604052565b601f909101601f19168101906001600160401b0382119082101761154f57604052565b6001600160401b03811161154f5760051b60200190565b91604091949360ff9160608501968552602085015216910152565b6001600160a01b039091168152602081019190915260400190565b611640611645939261163260405194859263a9059cbb60e01b6020850152602484016115f0565b03601f19810184528361159b565b611647565b565b60018060a01b031690600080826020829451910182865af13d1561170e573d906001600160401b0382116116fa57906116a291604051916116926020601f19601f840116018461159b565b82523d84602084013e5b8461171a565b9081519182151592836116d2575b5050506116ba5750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261024f576020015190811591821503610ff057503880806116b0565b634e487b7160e01b83526041600452602483fd5b6116a29060609061169c565b90611741575080511561172f57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580611774575b611752575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561174a565b60ff60015460a01c1661178c57565b60405163d93c066560e01b8152600490fd5b6002600054146117af576002600055565b604051633ee5aeb560e01b8152600490fd5b60609060208152600a6020820152693232b632b3b0ba34b7b760b11b60408201520190565b80546001600160a01b031660a09290921b6001600160a01b031916919091179055565b60008190039190600160ff1b81146001166115045760001983050361150457565b906001600160601b03908183116118ba576118466105ea6118cc565b9160018060a01b038092169283600052602052604060002090808516825460a01c0390811161150457600080516020611d26833981519152928261188f6118b1936020956117e6565b5416946118ac61189d61190a565b8761066f846109288385611afd565b611809565b604051908152a3565b60405163034c196960e31b8152600490fd5b604051906118d982611565565b60088252676465706f7369747360c01b6020830152565b604051906118fd82611565565b6000602083606081520152565b6119126118f0565b50604080519061192182611565565b600b825260206a3b37ba34b733a837bbb2b960a91b818401526119426118f0565b508151818101907f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a8252845184828560005b8481106119ad575050611996928101600083820152038581018452018261159b565b5190209151926119a584611565565b835282015290565b819350809250890101518782860101520182858792611974565b9091906001600160c01b038211611ad2576020908101516001600160a01b039093166000908152929052604082208054909290916001600160801b0391828416918183611aa1575b839043149182611a79575b4360c01b179087016001015515611a33575b5050505050565b60018201809211611a6557818460801c1015610ff057506001600160801b031990921691161790553880808080611a2c565b634e487b7160e01b81526011600452602490fd5b90506000198401848111611a8d5790611a1a565b634e487b7160e01b84526011600452602484fd5b506000198301838111611abe57611ab89087611c75565b50611a0f565b634e487b7160e01b83526011600452602483fd5b60405162461bcd60e51b815260206004820152600360248201526227b7a160e91b6044820152606490fd5b6020908101516001600160a01b0390921660009081529190526040902080546001600160801b03168015611b4557600019810190811161150457611b4091611c75565b905090565b5050600090565b919093928015611c405780821015611492576000199080820190811161150457919291905b818403611bc657505090611b8491611c75565b9210611b905760009190565b60405162461bcd60e51b815260206004820152600e60248201526d536561726368204661696c75726560901b6044820152606490fd5b611bd28285979561151a565b6001808201809211611c2b571c611be98185611c75565b9790868103611bff575050505050505060009190565b8698509590919293949510600014611c1d5750915b90929192611b71565b925081810190811115611c14575b60246000634e487b7160e01b81526011600452fd5b60405162461bcd60e51b815260206004820152600d60248201526c1d5b9a5b9a5d1a585b1a5e9959609a1b6044820152606490fd5b016001015460c081901c916001600160c01b0390911690565b604090815190602092838301937f03a912cdb153207069d92d44a2357e3f0ce00f7ee84da3510f1c6851b4cac4ee85528383516000945b818610611ced5750611ce794508101600084820152039081018452018261159b565b51902090565b85810184015187870186015294830194869250611cc556fe939e45c2a0bfec7a2bdfa3cb3e08e1bc7f8ba9dc5fe33aac70c75c949ebbb62433161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e05df5d723cf2b779a4bc7bbd4fa1082ab1a37397e0d8c408d7b2546c69585ee158be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220569cf0d808cac332c4c262a1767de84e52faa5b8c88a16ef53b90f81f01ff5a864736f6c63430008140033000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e

Deployed Bytecode

0x6080604081815260048036101561001557600080fd5b600092833560e01c9081630f0cb0b9146114245750806318160ddd14610f865780632cbb43fc146114065780632d984723146113e8578063375c22cd146113a45780633f4ba83a14611335578063441a3e701461128d578063506384171461121b5780635b6c508c146110ba5780635c975abb14611093578063600381f0146110755780636071105814610ff3578063715018a614610fa55780637d88209714610f865780637f8661a114610e6257806381a4fdd814610e1a5780638456cb5914610db957806387cef13014610aae5780638980f11f146109bb5780638da5cb5b146109925780639f973fd5146108ab578063b7ec8d4b1461054e578063d2d48ee3146103f0578063d6d681771461035f578063de1b905214610343578063deac03011461031c578063e7d20283146102f2578063e91f323514610253578063ea32f42e146101e75763f2fde38b1461016d57600080fd5b346101e35760203660031901126101e35761018661147c565b9061018f6114c9565b6001600160a01b039182169283156101cd575050600180546001600160a01b03198116841790915516600080516020611d668339815191528380a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8280fd5b83823461024f57602036600319011261024f5761020261147c565b61020a6114c9565b81546001600160a01b0319166001600160a01b03919091169081179091557fc35d72a054acf727dbcb414ab4b300b86c00481ccf567ab027bd57a0fb7727828280a280f35b5080fd5b50346101e35760603660031901126101e35761026d61147c565b90604435906001600160401b03908183116102ee57366023840112156102ee578201359081116102ea57369101602401116101e357816020936102e292856102b361190a565b01516001600160a01b03909116825285522080546001600160801b0381169160809190911c9060243590611b4c565b915191825250f35b8480fd5b8580fd5b50503461024f578060031936011261024f576102e28160209361031361147c565b856102b361190a565b50346101e357826003193601126101e3575490516001600160a01b03909116815260209150f35b50503461024f578160031936011261024f576020905160148152f35b5091903461024f578060031936011261024f5761037a61147c565b6001600160a01b031682526002602052808220805460243591908210156103ec57906103a591611497565b50908154600160ff82169301549282519460038210156103d95750845260081c63ffffffff16602084015282015260609150f35b634e487b7160e01b815260218752602490fd5b8380fd5b50346101e3576020806003193601126103ec57926001600160a01b0361041461147c565b168152600284528281209181908354835b81811061050e575061044e610439846115be565b936104468851958661159b565b8085526115be565b8388019590601f19013687378490855b8381106104a557505050505083519485948186019282875251809352850193925b82811061048e57505050500390f35b83518552869550938101939281019260010161047f565b60016104b782849c99989a9b9c611497565b5001546104d3575b6104c8906114f5565b98979694959861045e565b9187518110156104fb576104f38184896104c89460051b8c0101526114f5565b9290506104bf565b634e487b7160e01b865260328552602486fd5b600161051e82889a979698611497565b500154610539575b61052f906114f5565b9694929396610425565b9461054661052f916114f5565b959050610526565b5091903461024f57606036600319011261024f5760249283356001600160a01b038181169383359290918582036108a7576044359460038610156108a35761059461179e565b61059c61177d565b84156108955786156108775733885260209260028452848920549760148910156108675789918289610839575062278d00945b6001600160601b0390818a11610829576105ef6105ea6118cc565b611c8e565b8d3390528852888d2090815485828216958615610810575b505050831680940361080057908c969594939291818b16815460a01c019182116107ee5761067593929161063a916117e6565b61066f8a61064661190a565b936106518486611afd565b908c51838152600080516020611d268339815191528d3392a361151a565b916119c7565b3384526002855263ffffffff61068e878620944261151a565b169086519061069c82611534565b6107dc5788815285810191825286810188815284549094600160401b8210156107ca57906106cf91600182018155611497565b9590956107b95750519160038310156107a7575092600161079d969593600080516020611d468339815191529a9b9c936107919664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff1916171783555191015561073b8660035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528460648201526064815261076b81611580565b7f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf611647565b519283923396846115d5565b0390a26001815580f35b634e487b7160e01b8b52602190528a8afd5b634e487b7160e01b81528084528c90fd5b634e487b7160e01b8752604185528d87fd5b634e487b7160e01b8552602183528b85fd5b634e487b7160e01b8852601186528e88fd5b885163be6422b760e01b81528690fd5b6001600160a01b03199092161783559350388581610607565b885163034c196960e31b81528690fd5b8b935060018a0361084f5750624f1a00946105cf565b948b93506001198a016105cf5762c5c10095506105cf565b85516308f0bb4160e21b81528390fd5b8351635a8af66b60e01b81529081906108919082016117c1565b0390fd5b8351633e9842ab60e01b8152fd5b8780fd5b8680fd5b5090346101e35760203660031901126101e3576108c661147c565b6001600160a01b0381811693909290841561097857506108e76105ea6118cc565b33865260205280852080546001600160a01b03198116861790915560a081901c93169161096c9061091661190a565b9061093461092d876109288886611afd565b611527565b86846119c7565b61093d86611809565b948451958652600080516020611d26833981519152958660203392a361066f866109678385611afd565b61151a565b5191825260203392a380f35b9051635a8af66b60e01b81529081906108919082016117c1565b50503461024f578160031936011261024f5760015490516001600160a01b039091168152602090f35b5082903461024f578260031936011261024f576109d661147c565b602435916109e26114c9565b6001600160a01b0382811691907f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf168214610aa0578115610a74578315610a6657507f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289394610a5684610a6093339061160b565b51928392836115f0565b0390a180f35b8551633e9842ab60e01b8152fd5b6064906020875191635a8af66b60e01b835282015260056024820152643a37b5b2b760d91b6044820152fd5b85516305c6159560e31b8152fd5b5082903461024f57608036600319011261024f57610aca61147c565b602493843591604435916001600160a01b03808416908185036108a357606435936003851015610db557818854163303610da557610b0661179e565b610b0e61177d565b8615610d95578215610d7d5781169687895260209260028452848a2054966014881015610867578a918288610d4f575062278d00945b6001600160601b0390818c1161082957888e8e610b626105ea6118cc565b9082528a5220908154818116958615610d36575b5050841680930361080057908d969594939291818d16815460a01c01918211610d24578c8a8f94610bae610bdc979561066f956117e6565b610bb661190a565b95600080516020611d268339815191528d610bd1888a611afd565b9451868152a361151a565b8984526002855263ffffffff610bf5878620944261151a565b1690865190610c0382611534565b610d12578781528581019182528681018a815284549094600160401b821015610d005790610c3691600182018155611497565b959095610cef575051916003831015610cdd575092600161079d97969593600080516020611d468339815191529b9c9d93610cd39664ffffffff0060ff86549316918260ff1985161787555160081b169164ffffffffff19161717835551910155610ca38860035461151a565b6003558351916323b872dd60e01b9083015233908201523060448201528660648201526064815261076b81611580565b51938493846115d5565b634e487b7160e01b8c52602190528b8bfd5b634e487b7160e01b81528084528d90fd5b634e487b7160e01b8752604185528e87fd5b634e487b7160e01b8552602183528c85fd5b634e487b7160e01b8852601186528f88fd5b6001600160a01b03199091168517835594503880610b76565b8c935060018903610d655750624f1a0094610b44565b948c93506001198901610b445762c5c1009550610b44565b8351635a8af66b60e01b815280610891818b016117c1565b8351633e9842ab60e01b81528890fd5b83516303b22c3f60e51b81528890fd5b8880fd5b50503461024f578160031936011261024f5760207f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25891610df76114c9565b610dff61177d565b6001805460ff60a01b1916600160a01b17905551338152a180f35b50503461024f578060031936011261024f57602091600190610e58906001600160a01b03610e4661147c565b16815260028552836024359120611497565b5001549051908152f35b5091903461024f57602036600319011261024f57823590610e8161177d565b610e8961179e565b6000193384526002602052610ea083838620611497565b5090600182019182548015610f765763ffffffff825460081c164210610f6757808311610f5e575b5060ff9192610ed7338561182a565b610ee2848254611527565b9055610ef083600354611527565b600355610f1e83337f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf61160b565b5416916003831015610f4b579061079d600080516020611d068339815191529392519283923396846115d5565b634e487b7160e01b855260218652602485fd5b915060ff610ec8565b845162b4ffe560e41b81528890fd5b845163460e84d760e11b81528890fd5b50503461024f578160031936011261024f576020906003549051908152f35b8334610ff05780600319360112610ff057610fbe6114c9565b600180546001600160a01b0319811690915581906001600160a01b0316600080516020611d668339815191528280a380f35b80fd5b5091903461024f578060031936011261024f576001600160a01b0361101661147c565b168252600260205261102c602435828420611497565b509081549160ff8316936003851015611062575060609450600163ffffffff91015492825194855260081c166020840152820152f35b634e487b7160e01b815260218652602490fd5b50503461024f578160031936011261024f5760209051624f1a008152f35b50503461024f578160031936011261024f5760209060ff60015460a01c1690519015158152f35b5090346101e357826003193601126101e357906110d561177d565b6110dd61179e565b338352600260205281832080548493849392845b8381106111615787868880611150575b5080611110575b506001815580f35b8061112061114a92600354611527565b600355337f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf61160b565b81611108565b61115b90339061182a565b82611101565b61116b8183611497565b5096600188019081549182158015611208575b6111fa578261119f8160ff946111a5946111988380611527565b905561151a565b9961151a565b98541660038110156111e75790600080516020611d068339815191526111d76111df94938751918291339587846115d5565b0390a26114f5565b9594956110f1565b634e487b7160e01b8a526021875260248afd5b50969750506111df906114f5565b5063ffffffff8a5460081c16421061117e565b50503461024f57602036600319011261024f5761123661147c565b6001600160a01b03168252600260205280822080549092919082905b808210611263576020848451908152f35b90926112816112879160016112788789611497565b5001549061151a565b936114f5565b90611252565b5091903461024f578060031936011261024f576024359083356112ae61177d565b6112b661179e565b8081156113255733855260026020526112d184848720611497565b50600181019283549081156113155763ffffffff835460081c164210611306578110610f5e575060ff9192610ed7338561182a565b855162b4ffe560e41b81528990fd5b855163460e84d760e11b81528990fd5b8251633e9842ab60e01b81528690fd5b50346101e357826003193601126101e35761134e6114c9565b6001549060ff8260a01c1615611396575060ff60a01b1916600155513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a180f35b8251638dfc202b60e01b8152fd5b50503461024f578160031936011261024f57517f000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf6001600160a01b03168152602090f35b50503461024f578160031936011261024f576020905162278d008152f35b50503461024f578160031936011261024f576020905162c5c1008152f35b905083833461024f57602036600319011261024f576001600160a01b0361144961147c565b168252600260205281205460001981019190821161146957602083838152f35b634e487b7160e01b815260118452602490fd5b600435906001600160a01b038216820361149257565b600080fd5b80548210156114b35760005260206000209060011b0190600090565b634e487b7160e01b600052603260045260246000fd5b6001546001600160a01b031633036114dd57565b60405163118cdaa760e01b8152336004820152602490fd5b60001981146115045760010190565b634e487b7160e01b600052601160045260246000fd5b9190820180921161150457565b9190820391821161150457565b606081019081106001600160401b0382111761154f57604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761154f57604052565b60a081019081106001600160401b0382111761154f57604052565b601f909101601f19168101906001600160401b0382119082101761154f57604052565b6001600160401b03811161154f5760051b60200190565b91604091949360ff9160608501968552602085015216910152565b6001600160a01b039091168152602081019190915260400190565b611640611645939261163260405194859263a9059cbb60e01b6020850152602484016115f0565b03601f19810184528361159b565b611647565b565b60018060a01b031690600080826020829451910182865af13d1561170e573d906001600160401b0382116116fa57906116a291604051916116926020601f19601f840116018461159b565b82523d84602084013e5b8461171a565b9081519182151592836116d2575b5050506116ba5750565b60249060405190635274afe760e01b82526004820152fd5b81929350906020918101031261024f576020015190811591821503610ff057503880806116b0565b634e487b7160e01b83526041600452602483fd5b6116a29060609061169c565b90611741575080511561172f57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580611774575b611752575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561174a565b60ff60015460a01c1661178c57565b60405163d93c066560e01b8152600490fd5b6002600054146117af576002600055565b604051633ee5aeb560e01b8152600490fd5b60609060208152600a6020820152693232b632b3b0ba34b7b760b11b60408201520190565b80546001600160a01b031660a09290921b6001600160a01b031916919091179055565b60008190039190600160ff1b81146001166115045760001983050361150457565b906001600160601b03908183116118ba576118466105ea6118cc565b9160018060a01b038092169283600052602052604060002090808516825460a01c0390811161150457600080516020611d26833981519152928261188f6118b1936020956117e6565b5416946118ac61189d61190a565b8761066f846109288385611afd565b611809565b604051908152a3565b60405163034c196960e31b8152600490fd5b604051906118d982611565565b60088252676465706f7369747360c01b6020830152565b604051906118fd82611565565b6000602083606081520152565b6119126118f0565b50604080519061192182611565565b600b825260206a3b37ba34b733a837bbb2b960a91b818401526119426118f0565b508151818101907f7b1a68ec3e3284b167e69db1c622dcfa612281976b71d7e2d239dbe16a75891a8252845184828560005b8481106119ad575050611996928101600083820152038581018452018261159b565b5190209151926119a584611565565b835282015290565b819350809250890101518782860101520182858792611974565b9091906001600160c01b038211611ad2576020908101516001600160a01b039093166000908152929052604082208054909290916001600160801b0391828416918183611aa1575b839043149182611a79575b4360c01b179087016001015515611a33575b5050505050565b60018201809211611a6557818460801c1015610ff057506001600160801b031990921691161790553880808080611a2c565b634e487b7160e01b81526011600452602490fd5b90506000198401848111611a8d5790611a1a565b634e487b7160e01b84526011600452602484fd5b506000198301838111611abe57611ab89087611c75565b50611a0f565b634e487b7160e01b83526011600452602483fd5b60405162461bcd60e51b815260206004820152600360248201526227b7a160e91b6044820152606490fd5b6020908101516001600160a01b0390921660009081529190526040902080546001600160801b03168015611b4557600019810190811161150457611b4091611c75565b905090565b5050600090565b919093928015611c405780821015611492576000199080820190811161150457919291905b818403611bc657505090611b8491611c75565b9210611b905760009190565b60405162461bcd60e51b815260206004820152600e60248201526d536561726368204661696c75726560901b6044820152606490fd5b611bd28285979561151a565b6001808201809211611c2b571c611be98185611c75565b9790868103611bff575050505050505060009190565b8698509590919293949510600014611c1d5750915b90929192611b71565b925081810190811115611c14575b60246000634e487b7160e01b81526011600452fd5b60405162461bcd60e51b815260206004820152600d60248201526c1d5b9a5b9a5d1a585b1a5e9959609a1b6044820152606490fd5b016001015460c081901c916001600160c01b0390911690565b604090815190602092838301937f03a912cdb153207069d92d44a2357e3f0ce00f7ee84da3510f1c6851b4cac4ee85528383516000945b818610611ced5750611ce794508101600084820152039081018452018261159b565b51902090565b85810184015187870186015294830194869250611cc556fe939e45c2a0bfec7a2bdfa3cb3e08e1bc7f8ba9dc5fe33aac70c75c949ebbb62433161cf2da28d747be9df136b6f3729390298494947268743193c53d73d3c2e05df5d723cf2b779a4bc7bbd4fa1082ab1a37397e0d8c408d7b2546c69585ee158be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a2646970667358221220569cf0d808cac332c4c262a1767de84e52faa5b8c88a16ef53b90f81f01ff5a864736f6c63430008140033

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

000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e

-----Decoded View---------------
Arg [0] : _owner (address): 0x398e92C827C5FA0F33F171DC8E20570c5CfF330e
Arg [1] : _arcd (address): 0xe020B01B6fbD83066aa2e8ee0CCD1eB8d9Cc70bF
Arg [2] : _airdropDistribution (address): 0x398e92C827C5FA0F33F171DC8E20570c5CfF330e

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e
Arg [1] : 000000000000000000000000e020b01b6fbd83066aa2e8ee0ccd1eb8d9cc70bf
Arg [2] : 000000000000000000000000398e92c827c5fa0f33f171dc8e20570c5cff330e


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.