ETH Price: $2,334.84 (-0.58%)

Contract

0xf01B9A42a5AbC3A9f83b361Af92460ec53835Afa
 

Overview

ETH Balance

74 wei

Eth Value

Less Than $0.01 (@ $2,334.84/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update State180336272023-08-31 9:21:35384 days ago1693473695IN
0xf01B9A42...c53835Afa
0 ETH0.0044885913.30561392
End Game180335302023-08-31 9:02:11384 days ago1693472531IN
0xf01B9A42...c53835Afa
1 wei0.0022000614.81304942
Start Game180335282023-08-31 9:01:47384 days ago1693472507IN
0xf01B9A42...c53835Afa
0 ETH0.0014338814.5643404
Claim Share180280472023-08-30 14:35:47385 days ago1693406147IN
0xf01B9A42...c53835Afa
0 ETH0.0011462241.00973604
Claim Share180280472023-08-30 14:35:47385 days ago1693406147IN
0xf01B9A42...c53835Afa
0 ETH0.0010367337.09249605
Claim Share180280472023-08-30 14:35:47385 days ago1693406147IN
0xf01B9A42...c53835Afa
0 ETH0.0021351236.01268778
Claim Share180280162023-08-30 14:29:35385 days ago1693405775IN
0xf01B9A42...c53835Afa
0 ETH0.0022463337.88852489
Claim Share180218322023-08-29 17:42:59386 days ago1693330979IN
0xf01B9A42...c53835Afa
0 ETH0.0033315453.91375458
Claim Share180218292023-08-29 17:42:23386 days ago1693330943IN
0xf01B9A42...c53835Afa
0 ETH0.0026550353.91375458
Claim Share180216762023-08-29 17:11:47386 days ago1693329107IN
0xf01B9A42...c53835Afa
0 ETH0.0035023571.11967712
Start Vote180216342023-08-29 17:03:23386 days ago1693328603IN
0xf01B9A42...c53835Afa
0 ETH0.0034721158.90632301
Update State180216222023-08-29 17:00:59386 days ago1693328459IN
0xf01B9A42...c53835Afa
0 ETH0.1015460870.6628266
Transfer180215962023-08-29 16:55:47386 days ago1693328147IN
0xf01B9A42...c53835Afa
6.9 ETH0.0014598355.81038472
Transfer180215862023-08-29 16:53:47386 days ago1693328027IN
0xf01B9A42...c53835Afa
0.1 ETH0.0026630261.5629535
Set Period180206882023-08-29 13:52:11386 days ago1693317131IN
0xf01B9A42...c53835Afa
0 ETH0.0008941528.04563377
End Game180206842023-08-29 13:51:23386 days ago1693317083IN
0xf01B9A42...c53835Afa
1 wei0.0038911726.1993398
Start Game180206812023-08-29 13:50:47386 days ago1693317047IN
0xf01B9A42...c53835Afa
0 ETH0.0024553324.93938668
Start Vote180206732023-08-29 13:49:11386 days ago1693316951IN
0xf01B9A42...c53835Afa
0 ETH0.0018494229.28392599
Set Redeem Fee180206662023-08-29 13:47:47386 days ago1693316867IN
0xf01B9A42...c53835Afa
0 ETH0.0007662630.7316524
Set Period180206642023-08-29 13:47:23386 days ago1693316843IN
0xf01B9A42...c53835Afa
0 ETH0.0009926131.15739179
Set Period180206382023-08-29 13:42:11386 days ago1693316531IN
0xf01B9A42...c53835Afa
0 ETH0.000838926.3128768
Update State180144622023-08-28 16:54:47387 days ago1693241687IN
0xf01B9A42...c53835Afa
0 ETH0.0216492934.91568348
Transfer180144562023-08-28 16:53:35387 days ago1693241615IN
0xf01B9A42...c53835Afa
0.01 ETH0.0008589932.84008378
End Game180144392023-08-28 16:50:11387 days ago1693241411IN
0xf01B9A42...c53835Afa
1 wei0.0059831935.52499248
Start Game180143772023-08-28 16:37:23387 days ago1693240643IN
0xf01B9A42...c53835Afa
0 ETH0.0037498736.04398966
View all transactions

Latest 8 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
180336272023-08-31 9:21:35384 days ago1693473695
0xf01B9A42...c53835Afa
6.84444444 ETH
180280472023-08-30 14:35:47385 days ago1693406147
0xf01B9A42...c53835Afa
0.03888888 ETH
180280162023-08-30 14:29:35385 days ago1693405775
0xf01B9A42...c53835Afa
0.03888888 ETH
180218322023-08-29 17:42:59386 days ago1693330979
0xf01B9A42...c53835Afa
0.03888888 ETH
180218292023-08-29 17:42:23386 days ago1693330943
0xf01B9A42...c53835Afa
0.03888888 ETH
180216762023-08-29 17:11:47386 days ago1693329107
0xf01B9A42...c53835Afa
0.61 ETH
180144622023-08-28 16:54:47387 days ago1693241687
0xf01B9A42...c53835Afa
6.86979999 ETH
180143772023-08-28 16:37:23387 days ago1693240643
0xf01B9A42...c53835Afa
0.1402 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vote

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 888 runs

Other Settings:
default evmVersion
File 1 of 5 : Vote.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.18;

import "./interfaces/IVote.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/solady/src/auth/Ownable.sol";
import "lib/solady/src/utils/SafeTransferLib.sol";

contract Vote is IVote, Ownable {
    uint256 constant _PRECISION = 1e18;
    uint256 private constant _PERCENTAGE = 10000;
    uint256 private constant _TOTAL_SUPPLY = 1e26; // 100,000,000 w/ 18 decimals
    address private immutable _token;

    uint256 public votingPeriod;
    uint256 public lastRevenue;

    SnapshotStruct _snapshot;
    VoteStruct _currentVote;
    GameStruct _currentGame;

    uint256[] public counter;

    TierStruct[] public tiers;

    mapping(address => uint256) public balanceOf;
    mapping(address => uint256) public tierOf;

    mapping(address => uint256) public gameShareOf;

    mapping(address => uint256) public lastActionOf;

    mapping(uint256 => GameStruct) public voteTimestampToGame;

    uint256 public redeemFee;
    address payable _protocolAddress;
    address _updateAddress;

    constructor(address _tokenAddress) noZeroAddress(_tokenAddress) {
        _initializeOwner(msg.sender);
        _token = _tokenAddress;
    }

    function castVote(uint8 _choice) external inSession {
        VoteStruct storage localVote = _currentVote;
        if (_choice > localVote.choices) revert NotAChoice(localVote.choices - 1);

        uint256 amount = _getTierAmount(msg.sender);
        amount += getReward(msg.sender);
        //slither-disable-next-line incorrect-equality
        if (amount == 0) return;

        counter[_choice] += 1;

        balanceOf[msg.sender] = 0;
        lastActionOf[msg.sender] = localVote.timestamp;
        emit VoteCasted(msg.sender, _choice);
        _currentGame.balanceBefore += amount;
        gameShareOf[msg.sender] = amount;
    }

    function claimShare() external inSession {
        uint256 amount = _getTierAmount(msg.sender);
        //slither-disable-next-line incorrect-equality
        if (amount == 0) return;

        uint256 toProtocol = amount * redeemFee / _PERCENTAGE;
        amount -= toProtocol;
        amount += getReward(msg.sender);

        balanceOf[msg.sender] = 0;
        gameShareOf[msg.sender] = 0;
        emit ShareClaimed(msg.sender, amount, toProtocol);
        SafeTransferLib.safeTransferETH(msg.sender, amount);
        SafeTransferLib.safeTransferETH(_protocolAddress, toProtocol);
    }

    function claimReward() external requireVoteNotActive requireGameNotActive {
        uint256 amount = getReward(msg.sender);
        if (amount == 0 && gameShareOf[msg.sender] == 0) revert NoReward();

        gameShareOf[msg.sender] = 0;
        emit RewardClaimed(msg.sender, amount);
        SafeTransferLib.safeTransferETH(msg.sender, amount);
    }

    function _getTierAmount(address _user) internal returns (uint256) {
        uint256 currentBalance = IERC20(_token).balanceOf(_user);

        int256 currentTier = _findTier(currentBalance);
        uint256 amount = balanceOf[_user];

        if (currentTier < 0) {
            balanceOf[_user] = 0;
            emit ForfeitShare(msg.sender, amount);
            SafeTransferLib.safeTransferETH(_protocolAddress, amount);
            return 0;
        }

        uint256 snapshotTier = tierOf[_user];
        uint256 currentTierUnsigned = uint256(currentTier);
        if (snapshotTier > currentTierUnsigned) {
            uint256 currentTierShare = _getTierRevenue(currentTierUnsigned) / _snapshot.usersByTier[currentTierUnsigned];
            uint256 snapshotTierShare = _getTierRevenue(snapshotTier) / _snapshot.usersByTier[snapshotTier];
            uint256 toProtocol = snapshotTierShare - currentTierShare;
            amount -= toProtocol;
            emit ForfeitSharePartial(msg.sender, amount, toProtocol);
            SafeTransferLib.safeTransferETH(_protocolAddress, toProtocol);
        }
        return amount;
    }

    function _getTierRevenue(uint256 _tier) internal view returns (uint256) {
        return _currentVote.balance * tiers[_tier].revenueShare / _PERCENTAGE;
    }

    /// @notice Starts a vote session
    /// @dev If vote is not already undergoing the contractOwner can start a vote session,
    /// we store number of choices, timestamp of action and set vote.active to true;
    /// function is set to payable to cut on gas costs
    /// @param _choices indicates the number of choices available for this vote sessions
    /// Note Emits a {VoteStarted} event
    function startVote(uint8 _choices) external payable onlyOwnerOrUpdate requireVoteNotActive {
        if (_choices < 2) revert NoChoices();
        _currentVote = VoteStruct(true, _choices, _snapshot.balance, block.timestamp);
        _currentGame = GameStruct(false, 0, 0, 0, 0);
        counter = new uint256[](_choices);

        emit VoteStarted(_choices);
    }

    function _getWinners() internal view returns (uint256[] memory) {
        uint256[] memory localCounter = counter;
        uint256 counterLength = localCounter.length;
        uint256 maxNumber = localCounter[0];
        uint256 count = 1;

        for (uint256 i = 1; i < counterLength; ++i) {
            if (localCounter[i] > maxNumber) {
                maxNumber = localCounter[i];
                count = 1;
            } else if (localCounter[i] == maxNumber) {
                ++count;
            }
        }

        uint256[] memory winners = new uint256[](count);
        count = 0;

        for (uint256 i; i < counterLength; ++i) {
            if (localCounter[i] == maxNumber) {
                winners[count] = i;
                count += 1;
            }
        }

        return winners;
    }

    function _findTier(uint256 balance) internal view returns (int256) {
        uint256 length = tiers.length;
        for (uint256 i; i < length; ++i) {
            if (balance < tiers[i].tokenAmount) {
                return (i == 0) ? -1 : int256(i) - 1;
            }
        }
        return int256(length) - 1;
    }

    function updateState(address[][] memory addressByTier) external payable onlyUpdate requireVoteNotActive {
        SnapshotStruct storage localSnapshot = _snapshot;
        if (localSnapshot.timestamp > _currentVote.timestamp) revert NoConsecutiveSnapshot();
        TierStruct[] memory localTiers = tiers;
        uint256 tiersLength = localTiers.length;
        if (addressByTier.length != tiersLength + 1) revert TierArrayLength(tiersLength + 1);
        delete _snapshot;
        uint256 toProtocol;

        // first array is reserved for addresses that do not respect any threshold requirement
        // we zero out the pending rewards for them and send them to protocol wallet
        uint256 zeroLength = addressByTier[0].length;
        for (uint256 i; i < zeroLength; ++i) {
            toProtocol += balanceOf[addressByTier[0][i]];
            balanceOf[addressByTier[0][i]] = 0;
        }
        SafeTransferLib.safeTransferETH(_protocolAddress, toProtocol);

        uint256 inputLength = addressByTier.length;
        _snapshot = SnapshotStruct(new uint256[](tiersLength), lastRevenue, block.timestamp);

        for (uint256 l = 1; l < inputLength; ++l) {
            uint256 tier = l - 1;
            uint256 tierLength = addressByTier[l].length;
            localSnapshot.usersByTier[tier] = tierLength;
            uint256 individualShare = (lastRevenue * localTiers[tier].revenueShare / _PERCENTAGE) / tierLength;

            // Update the state for each user
            for (uint256 i; i < tierLength; ++i) {
                tierOf[addressByTier[l][i]] = tier;
                balanceOf[addressByTier[l][i]] += individualShare;
            }
        }
        lastRevenue = 0;
        // maybe emit event?
    }

    function startGame() external payable onlyOwnerOrUpdate requireGameNotActive {
        VoteStruct storage localVote = _currentVote;
        GameStruct storage localGame = _currentGame;
        if (block.timestamp < localVote.timestamp + votingPeriod) revert OutOfBounds();
        if (!localVote.active) revert VoteNotActive();

        uint256[] memory winners = _getWinners();
        localVote.active = false;
        localGame.active = true;
        localGame.withdrawTimestamp = block.timestamp;
        emit VoteEnded(owner(), winners, localGame.balanceBefore);
        SafeTransferLib.safeTransferETH(owner(), localGame.balanceBefore);
    }

    function endGame() external payable onlyOwner requireGameActive requireVoteNotActive {
        GameStruct storage localGame = _currentGame;
        localGame.active = false;
        localGame.balanceAfter = msg.value;
        localGame.depositTimestamp = block.timestamp;

        voteTimestampToGame[_currentVote.timestamp] = localGame;
        emit GameEnded(msg.value);
    }

    function getReward(address _address) public view returns (uint256) {
        uint256 share = gameShareOf[_address];
        if (share == 0) return 0;
        GameStruct storage localGame = voteTimestampToGame[lastActionOf[_address]];
        uint256 multiplier = _getMultiplier(localGame.balanceAfter, localGame.balanceBefore);
        return share * multiplier / _PRECISION;
    }

    function _getMultiplier(uint256 _newBalance, uint256 _oldBalance) internal pure returns (uint256) {
        return (_newBalance * _PRECISION) / _oldBalance;
    }

    function setTiers(TierStruct[] calldata _tiers) external payable onlyOwner tiersSafe(_tiers) {
        delete tiers;
        uint256 length = _tiers.length;
        for (uint256 i; i < length; ++i) {
            tiers.push(_tiers[i]);
        }

        emit TiersSet(_tiers);
    }

    function setPeriod(uint256 _period) external payable onlyOwner requireVoteNotActive {
        votingPeriod = _period;
        emit VotingPeriodSet(_period);
    }

    function setProtocolAddress(address _address) external payable onlyOwner noZeroAddress(_address) {
        _protocolAddress = payable(_address);
        emit ProtocolAddressSet(_address);
    }

    function setUpdateAddress(address _address) external payable onlyOwner noZeroAddress(_address) {
        _updateAddress = _address;
        emit UpdateAddressSet(_address);
    }

    /// @notice Sets fee for early redeem, values from 0.01 (1) to 100.00 (10000)
    /// @param _fee new fee amount, a percentage expressed in uint256 with 2 decimals
    function setRedeemFee(uint256 _fee) external payable onlyOwner {
        if (_fee > _PERCENTAGE) revert FeeOverflow();
        redeemFee = _fee;
        emit RedeemFeeSet(_fee);
    }

    function renounceOwnership() public payable override onlyOwner {
        revert NoRenounce();
    }

    receive() external payable {
        lastRevenue += msg.value;
    }

    // redeem emergency function;

    function currentVote() external view returns (VoteStruct memory) {
        return _currentVote;
    }

    function currentGame() external view returns (GameStruct memory) {
        return _currentGame;
    }

    function snapshot() external view returns (SnapshotStruct memory) {
        return _snapshot;
    }

    function getUsersByTier() external view returns (uint256[] memory) {
        return _snapshot.usersByTier;
    }

    modifier inSession() {
        VoteStruct storage localVote = _currentVote;
        if (block.timestamp < localVote.timestamp || block.timestamp > localVote.timestamp + votingPeriod) {
            revert OutOfBounds();
        }

        if (balanceOf[msg.sender] == 0) revert NoReward();

        _;
    }

    modifier tiersSafe(TierStruct[] memory _tiers) {
        uint256 length = _tiers.length;
        if (length == 0) revert NoTiers();
        uint256 tokenTotal;
        uint256 revenueTotal;
        uint256 supply = _TOTAL_SUPPLY;
        for (uint256 i; i < length; ++i) {
            tokenTotal += _tiers[i].tokenAmount;
            revenueTotal += _tiers[i].revenueShare;

            if (tokenTotal > supply) revert TokenTierOverflow();
            if (revenueTotal > 10_000) revert ShareTierOverflow();
        }

        _;
    }

    modifier noZeroAddress(address _address) {
        if (_address == address(0)) revert ZeroAddress();
        _;
    }

    modifier requireVoteNotActive() {
        if (_currentVote.active) revert VoteActive();
        _;
    }

    modifier requireGameActive() {
        if (!_currentGame.active) revert GameNotActive();
        _;
    }

    modifier requireGameNotActive() {
        if (_currentGame.active) revert GameActive();
        _;
    }

    modifier onlyUpdate() {
        if (msg.sender != _updateAddress) revert OnlyUpdate();
        _;
    }

    modifier onlyOwnerOrUpdate() {
        if (msg.sender != owner() && msg.sender != _updateAddress) revert NotOwnerOrUpdate();
        _;
    }
}

File 2 of 5 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 3 of 5 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 4 of 5 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for gas griefing protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

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

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

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

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

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    /// Multiply by a small constant (e.g. 2), if needed.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

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

    /// @dev Sends `amount` (in wei) ETH to `to`.
    /// Reverts upon failure.
    ///
    /// Note: This implementation does NOT protect against gas griefing.
    /// Please use `forceSafeTransferETH` for gas griefing protection.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // To coerce gas estimation to provide enough gas for the `create` above.
                    if iszero(gt(gas(), 1000000)) { revert(0x00, 0x00) }
                }
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
    /// equal to `GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
    /// for 99% of cases and can be overridden with the three-argument version of this
    /// function if necessary.
    ///
    /// If sending via the normal procedure fails, force sends the ETH by
    /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
    ///
    /// Reverts if the current contract has insufficient balance.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        // Manually inlined because the compiler doesn't inline functions with branches.
        /// @solidity memory-safe-assembly
        assembly {
            // If insufficient balance, revert.
            if lt(selfbalance(), amount) {
                // Store the function selector of `ETHTransferFailed()`.
                mstore(0x00, 0xb12d13eb)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                // We can directly use `SELFDESTRUCT` in the contract creation.
                // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
                if iszero(create(amount, 0x0b, 0x16)) {
                    // To coerce gas estimation to provide enough gas for the `create` above.
                    if iszero(gt(gas(), 1000000)) { revert(0x00, 0x00) }
                }
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// The `gasStipend` can be set to a low enough value to prevent
    /// storage writes or gas griefing.
    ///
    /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
    ///
    /// Note: Does NOT revert upon failure.
    /// Returns whether the transfer of ETH is successful instead.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and check if it succeeded or not.
            success := call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x0c, 0x23b872dd000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.

            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            // Store the function selector of `balanceOf(address)`.
            mstore(0x0c, 0x70a08231000000000000000000000000)
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Store the function selector of `transferFrom(address,address,uint256)`.
            mstore(0x00, 0x23b872dd)
            // The `amount` is already at 0x60. Load it for the function's return value.
            amount := mload(0x60)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFromFailed()`.
                mstore(0x00, 0x7939f424)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            mstore(0x14, to) // Store the `to` argument.
            // The `amount` is already at 0x34. Load it for the function's return value.
            amount := mload(0x34)
            // Store the function selector of `transfer(address,uint256)`.
            mstore(0x00, 0xa9059cbb000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `TransferFailed()`.
                mstore(0x00, 0x90b8ec18)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`.
            mstore(0x00, 0x095ea7b3000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                // Store the function selector of `ApproveFailed()`.
                mstore(0x00, 0x3e3f8f73)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            // Store the function selector of `approve(address,uint256)`.
            mstore(0x00, 0x095ea7b3000000000000000000000000)

            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(eq(mload(0x00), 1), iszero(returndatasize())),
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // Store the function selector.
                // We can ignore the result of this call. Just need to check the next call.
                pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00))
                mstore(0x34, amount) // Store back the original `amount`.

                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())),
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    // Store the function selector of `ApproveFailed()`.
                    mstore(0x00, 0x3e3f8f73)
                    // Revert with (offset, size).
                    revert(0x1c, 0x04)
                }
            }
            // Restore the part of the free memory pointer that was overwritten.
            mstore(0x34, 0)
        }
    }

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

File 5 of 5 : IVote.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.18;

/**
 * @dev Interface of the VoteShare.
 */
interface IVote {
    struct TierStruct {
        uint256 tokenAmount;
        uint256 revenueShare;
    }

    struct SnapshotStruct {
        uint256[] usersByTier;
        uint256 balance;
        uint256 timestamp;
    }

    struct VoteStruct {
        bool active;
        uint8 choices;
        uint256 balance;
        uint256 timestamp;
    }

    struct GameStruct {
        bool active;
        uint256 balanceBefore;
        uint256 withdrawTimestamp;
        uint256 balanceAfter;
        uint256 depositTimestamp;
    }

    event TiersSet(TierStruct[] _tiers);
    event VotingPeriodSet(uint256 _period);
    event ProtocolAddressSet(address _address);
    event UpdateAddressSet(address _address);
    event RedeemFeeSet(uint256 fee);
    event VoteStarted(uint8 _choices);
    event VoteEnded(address _sender, uint256[] _winners, uint256 _gameBalance);
    event VoteCasted(address _sender, uint8 _choice);
    event ShareClaimed(address _sender, uint256 _amount, uint256 _fee);
    event RewardClaimed(address _sender, uint256 _amount);
    event ForfeitShare(address _sender, uint256 _amount);
    event ForfeitSharePartial(address _sender, uint256 _amount, uint256 _toProtocol);
    event GameStarted(uint256 _balance);
    event GameEnded(uint256 _balance);

    /// @dev The new address cannot be the zero address.
    error ZeroAddress();

    /// @dev You are trying to complete an action that can be done only when no vote is in progress.
    error VoteActive();

    /// @dev as this contract relies on externally set variables we don't let the owner forfeit access
    error NoRenounce();

    /// @dev Fee exceeds the maximum of 100%.
    error FeeOverflow();

    /// @dev You are trying to complete an action that can be done only when vote is in progress.
    error VoteNotActive();

    /// @dev Action does not respect time limits dependent on currentVote.timestamp and votingPeriod
    error OutOfBounds();

    /// @dev The sum of the tokenAmount(s) of the tiers exceeds the maxSupply
    error TokenTierOverflow();

    /// @dev The sum of the revenueShare(s) of the tiers exceeds 100%
    error ShareTierOverflow();

    /// @dev The updateState input should be equal to number of tiers +1 (the downgrade array as index 0)
    /// @param tiers orrect number of arrays
    error TierArrayLength(uint256 tiers);

    /// @dev Choice should be a number between min and max
    /// @param max index of last choice, is currentVote.choices-1
    error NotAChoice(uint8 max);

    /// @dev Choice should be a number between min and max
    /// @param timestamp user lastAction timestamp
    error LastAction(uint256 timestamp);

    /// @dev User hasn't been assigned any reward tier in snapshot
    error NoTier();

    /// @dev you are trying to set an empty array of tiers;
    error NoTiers();

    /// @dev this function can be accessed only by Update address
    error OnlyUpdate();

    /// @dev user has no pending rewards
    error NoReward();

    /// @dev there aren't enough choices to start a vote
    error NoChoices();

    /// @dev No two consecutive snapshots before a vote
    error NoConsecutiveSnapshot();

    /// @dev You are trying to complete an action that can be done only when no game session is in progress.
    error GameActive();

    /// @dev You are trying to complete an action that can be done only when a game session is in progress.
    error GameNotActive();

    /// @dev Only owner or update address can call this function
    error NotOwnerOrUpdate();
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 888
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FeeOverflow","type":"error"},{"inputs":[],"name":"GameActive","type":"error"},{"inputs":[],"name":"GameNotActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"LastAction","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoChoices","type":"error"},{"inputs":[],"name":"NoConsecutiveSnapshot","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoRenounce","type":"error"},{"inputs":[],"name":"NoReward","type":"error"},{"inputs":[],"name":"NoTier","type":"error"},{"inputs":[],"name":"NoTiers","type":"error"},{"inputs":[{"internalType":"uint8","name":"max","type":"uint8"}],"name":"NotAChoice","type":"error"},{"inputs":[],"name":"NotOwnerOrUpdate","type":"error"},{"inputs":[],"name":"OnlyUpdate","type":"error"},{"inputs":[],"name":"OutOfBounds","type":"error"},{"inputs":[],"name":"ShareTierOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"tiers","type":"uint256"}],"name":"TierArrayLength","type":"error"},{"inputs":[],"name":"TokenTierOverflow","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VoteActive","type":"error"},{"inputs":[],"name":"VoteNotActive","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ForfeitShare","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toProtocol","type":"uint256"}],"name":"ForfeitSharePartial","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_balance","type":"uint256"}],"name":"GameEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_balance","type":"uint256"}],"name":"GameStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"ProtocolAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RedeemFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"ShareClaimed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"revenueShare","type":"uint256"}],"indexed":false,"internalType":"struct IVote.TierStruct[]","name":"_tiers","type":"tuple[]"}],"name":"TiersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"UpdateAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint8","name":"_choice","type":"uint8"}],"name":"VoteCasted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_winners","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"_gameBalance","type":"uint256"}],"name":"VoteEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"_choices","type":"uint8"}],"name":"VoteStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_period","type":"uint256"}],"name":"VotingPeriodSet","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_choice","type":"uint8"}],"name":"castVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentGame","outputs":[{"components":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balanceBefore","type":"uint256"},{"internalType":"uint256","name":"withdrawTimestamp","type":"uint256"},{"internalType":"uint256","name":"balanceAfter","type":"uint256"},{"internalType":"uint256","name":"depositTimestamp","type":"uint256"}],"internalType":"struct IVote.GameStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentVote","outputs":[{"components":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"choices","type":"uint8"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVote.VoteStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gameShareOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsersByTier","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastActionOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRevenue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeemFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_period","type":"uint256"}],"name":"setPeriod","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setProtocolAddress","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setRedeemFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"revenueShare","type":"uint256"}],"internalType":"struct IVote.TierStruct[]","name":"_tiers","type":"tuple[]"}],"name":"setTiers","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setUpdateAddress","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"snapshot","outputs":[{"components":[{"internalType":"uint256[]","name":"usersByTier","type":"uint256[]"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IVote.SnapshotStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_choices","type":"uint8"}],"name":"startVote","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tierOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tiers","outputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"revenueShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[][]","name":"addressByTier","type":"address[][]"}],"name":"updateState","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voteTimestampToGame","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balanceBefore","type":"uint256"},{"internalType":"uint256","name":"withdrawTimestamp","type":"uint256"},{"internalType":"uint256","name":"balanceAfter","type":"uint256"},{"internalType":"uint256","name":"depositTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b5060405162002821380380620028218339810160408190526200003491620000b7565b806001600160a01b0381166200005d5760405163d92e233d60e01b815260040160405180910390fd5b62000068336200007b565b506001600160a01b0316608052620000e9565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b600060208284031215620000ca57600080fd5b81516001600160a01b0381168114620000e257600080fd5b9392505050565b60805161271c620001056000396000611c18015261271c6000f3fe60806040526004361061021d5760003560e01c80638da5cb5b1161011d578063c8f74bb8116100b0578063f04e283e1161007f578063f2fde38b11610064578063f2fde38b1461060c578063f9b137d71461061f578063fee81cf4146106c357600080fd5b8063f04e283e146105d7578063f06d66ed146105ea57600080fd5b8063c8f74bb81461057c578063ca84bee0146105a9578063d65ab5f2146105bc578063daac7af7146105c457600080fd5b8063aabfec03116100ec578063aabfec0314610511578063b1f525c614610527578063b88a802f14610547578063c00007b01461055c57600080fd5b80638da5cb5b1461048d57806394a13541146104b9578063965fa21e146104d95780639711715a146104ef57600080fd5b806354d1f13d116101b0578063666da64f1161017f5780636cbc2ded116101645780636cbc2ded1461045057806370a0823114610458578063715018a61461048557600080fd5b8063666da64f146104285780636c6281981461043d57600080fd5b806354d1f13d146103a157806358e47004146103a95780635d841af5146103bc57806360b663bb146103cf57600080fd5b80631a9faa80116101ec5780631a9faa80146102e15780631aa62348146102f45780632569296214610321578063348b04f41461032957600080fd5b806302a251a314610241578063039af9eb1461026a578063085abff91461029f5780630f3a9f65146102cc57600080fd5b3661023c573460016000828254610234919061221d565b925050819055005b600080fd5b34801561024d57600080fd5b5061025760005481565b6040519081526020015b60405180910390f35b34801561027657600080fd5b5061028a610285366004612230565b6106f6565b60408051928352602083019190915201610261565b3480156102ab57600080fd5b506102576102ba366004612265565b60126020526000908152604090205481565b6102df6102da366004612230565b610724565b005b6102df6102ef366004612265565b61078c565b34801561030057600080fd5b5061025761030f366004612265565b60116020526000908152604090205481565b6102df61081f565b34801561033557600080fd5b50610377610344366004612230565b6013602052600090815260409020805460018201546002830154600384015460049094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a001610261565b6102df61086f565b6102df6103b7366004612265565b6108ab565b6102df6103ca366004612230565b610936565b3480156103db57600080fd5b506103e46109af565b6040516102619190600060a0820190508251151582526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b34801561043457600080fd5b506102df610a1d565b6102df61044b3660046122eb565b610b69565b6102df61105f565b34801561046457600080fd5b50610257610473366004612265565b600f6020526000908152604090205481565b6102df611149565b34801561049957600080fd5b50638b78c6d819546040516001600160a01b039091168152602001610261565b3480156104c557600080fd5b506102df6104d43660046123fe565b611183565b3480156104e557600080fd5b5061025760145481565b3480156104fb57600080fd5b5061050461134c565b6040516102619190612421565b34801561051d57600080fd5b5061025760015481565b34801561053357600080fd5b50610257610542366004612230565b6113e7565b34801561055357600080fd5b506102df611408565b34801561056857600080fd5b50610257610577366004612265565b6114eb565b34801561058857600080fd5b50610257610597366004612265565b60106020526000908152604090205481565b6102df6105b73660046123fe565b611578565b6102df611744565b6102df6105d2366004612487565b6118b2565b6102df6105e5366004612265565b611aff565b3480156105f657600080fd5b506105ff611b3c565b6040516102619190612537565b6102df61061a366004612265565b611b97565b34801561062b57600080fd5b5061068b604080516080810182526000808252602082018190529181018290526060810191909152506040805160808101825260055460ff8082161515835261010090910416602082015260065491810191909152600754606082015290565b604051610261919081511515815260208083015160ff1690820152604080830151908201526060918201519181019190915260800190565b3480156106cf57600080fd5b506102576106de366004612265565b63389a75e1600c908152600091909152602090205490565b600e818154811061070657600080fd5b60009182526020909120600290910201805460019091015490915082565b61072c611bbe565b60055460ff161561075057604051631b37a49b60e11b815260040160405180910390fd5b60008190556040518181527fc7f7e966c446ca07890210c0a7c9571d1d6f042fb01f27e51a921b2c3285de58906020015b60405180910390a150565b610794611bbe565b806001600160a01b0381166107bc5760405163d92e233d60e01b815260040160405180910390fd5b6016805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091556040519081527f74c630ccb1be1eeeb4a7b37a6042d7514309cb4db95249fcf21810e69b8e1f9b906020015b60405180910390a15050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6108b3611bbe565b806001600160a01b0381166108db5760405163d92e233d60e01b815260040160405180910390fd5b6015805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091556040519081527f1b95d297874c15a1a61f448b3d75e73df3e1db34cca1a51ea6a37e8f2afa5a8790602001610813565b61093e611bbe565b61271081111561097a576040517f8181adca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60148190556040518181527f5731b0070318c57b1e89e9f9b069ccc420b50d888b8c107793ecbde744b326c990602001610781565b6109e36040518060a00160405280600015158152602001600081526020016000815260200160008152602001600081525090565b506040805160a08101825260085460ff16151581526009546020820152600a5491810191909152600b546060820152600c54608082015290565b600754600590421080610a4057506000548160020154610a3d919061221d565b42115b15610a5e57604051632d0483c560e21b815260040160405180910390fd5b336000908152600f60205260408120549003610a8d5760405163374c934360e11b815260040160405180910390fd5b6000610a9833611bdb565b905080600003610aa6575050565b600061271060145483610ab9919061254a565b610ac39190612561565b9050610acf8183612583565b9150610ada336114eb565b610ae4908361221d565b336000818152600f6020908152604080832083905560118252808320929092558151928352820183905281018390529092507fa61ed5185dc0025e46a901a17312de0fd9453d957368006242930ed60391b4289060600160405180910390a1610b4d3383611e3c565b601554610b63906001600160a01b031682611e3c565b50505b50565b6016546001600160a01b03163314610bad576040517f02ca220300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460ff1615610bd157604051631b37a49b60e11b815260040160405180910390fd5b6007546004546002911015610c12576040517f78d1716100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600e805480602002602001604051908101604052809291908181526020016000905b82821015610c7c57838290600052602060002090600202016040518060400160405290816000820154815260200160018201548152505081526020019060010190610c36565b50508251929350610c929150829050600161221d565b845114610ce257610ca481600161221d565b6040517f0a2ab84c000000000000000000000000000000000000000000000000000000008152600401610cd991815260200190565b60405180910390fd5b60026000610cf0828261214d565b60018201600090556002820160009055505060008085600081518110610d1857610d18612596565b602002602001015151905060005b81811015610e0357600f600088600081518110610d4557610d45612596565b60200260200101518381518110610d5e57610d5e612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205483610d92919061221d565b92506000600f600089600081518110610dad57610dad612596565b60200260200101518481518110610dc657610dc6612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555080610dfc906125ac565b9050610d26565b50601554610e1a906001600160a01b031683611e3c565b85516040805160608101909152808567ffffffffffffffff811115610e4157610e41612280565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b50815260200160015481526020014281525060026000820151816000019080519060200190610e9a92919061216b565b5060208201516001808301919091556040909201516002909101555b81811015611050576000610ecb600183612583565b90506000898381518110610ee157610ee1612596565b602002602001015151905080896000018381548110610f0257610f02612596565b90600052602060002001819055506000816127108a8581518110610f2857610f28612596565b602002602001015160200151600154610f41919061254a565b610f4b9190612561565b610f559190612561565b905060005b8281101561103b5783601060008e8881518110610f7957610f79612596565b60200260200101518481518110610f9257610f92612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555081600f60008e8881518110610fd557610fd5612596565b60200260200101518481518110610fee57610fee612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254611025919061221d565b909155506110349050816125ac565b9050610f5a565b5050505080611049906125ac565b9050610eb6565b50506000600155505050505050565b611067611bbe565b60085460ff166110a2576040517ea3097100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460ff16156110c657604051631b37a49b60e11b815260040160405180910390fd5b6008805460ff19908116825534600b81815542600c908155600754600090815260136020908152604091829020805490961686556009546001870155600a54600287015592546003860155905460049094019390935591519081527fce24807f7e4b60b4e641462f13029fa5e5f79075bbc3f8e5cd43ecd19661d7609101610781565b611151611bbe565b6040517f0178ea2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005904210806111a6575060005481600201546111a3919061221d565b42115b156111c457604051632d0483c560e21b815260040160405180910390fd5b336000908152600f602052604081205490036111f35760405163374c934360e11b815260040160405180910390fd5b6005805460ff6101009091048116908416111561125857805461122090600190610100900460ff166125c5565b6040517f67a5681d00000000000000000000000000000000000000000000000000000000815260ff9091166004820152602401610cd9565b600061126333611bdb565b905061126e336114eb565b611278908261221d565b9050806000036112885750505050565b6001600d8560ff16815481106112a0576112a0612596565b9060005260206000200160008282546112b9919061221d565b9091555050336000818152600f602090815260408083208390556002860154601283529281902092909255815192835260ff8716908301527f76580ac7c171cd3d2029d56fd501d1351f2db92da148447b53227803bf940c02910160405180910390a18060086001016000828254611331919061221d565b909155505033600090815260116020526040902055505b5050565b61137060405180606001604052806060815260200160008152602001600081525090565b60408051600280546080602082028401810190945260608301818152929391928492909184918401828280156113c557602002820191906000526020600020905b8154815260200190600101908083116113b1575b5050505050815260200160018201548152602001600282015481525050905090565b600d81815481106113f757600080fd5b600091825260209091200154905081565b60055460ff161561142c57604051631b37a49b60e11b815260040160405180910390fd5b60085460ff16156114505760405163ad9a658360e01b815260040160405180910390fd5b600061145b336114eb565b905080158015611478575033600090815260116020526040902054155b156114965760405163374c934360e11b815260040160405180910390fd5b33600081815260116020908152604080832092909255815192835282018390527f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f7241910160405180910390a1610b663382611e3c565b6001600160a01b0381166000908152601160205260408120548082036115145750600092915050565b6001600160a01b038316600090815260126020908152604080832054835260139091528120600381015460018201549192916115509190611e58565b9050670de0b6b3a7640000611565828561254a565b61156f9190612561565b95945050505050565b638b78c6d819546001600160a01b0316336001600160a01b0316141580156115ab57506016546001600160a01b03163314155b156115c957604051636a7cb38760e01b815260040160405180910390fd5b60055460ff16156115ed57604051631b37a49b60e11b815260040160405180910390fd5b60028160ff16101561162b576040517f0db2f02f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352600180835260ff851660208085018290526003548587018190524260609687018190526005805461ffff1916610100860217909517909455600655600792909255845160a081018652600080825292810183905294850182905292840181905292018290526008805460ff191690556009829055600a829055600b829055600c9190915567ffffffffffffffff8111156116d2576116d2612280565b6040519080825280602002602001820160405280156116fb578160200160208202803683370190505b50805161171091600d9160209091019061216b565b5060405160ff821681527fbe58d0caad5cc482090345434dcddcd9ee5de5c8093d408bbbccec5ec3f259c590602001610781565b638b78c6d819546001600160a01b0316336001600160a01b03161415801561177757506016546001600160a01b03163314155b1561179557604051636a7cb38760e01b815260040160405180910390fd5b60085460ff16156117b95760405163ad9a658360e01b815260040160405180910390fd5b6000546007546005916008916117cf919061221d565b4210156117ef57604051632d0483c560e21b815260040160405180910390fd5b815460ff1661182a576040517fc878ebc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611834611e80565b835460ff199081168555835416600117835542600284015590507f8c2cde4db10afff6df05847c94cc85bd0b18815563d485e897585552dd14640461187c638b78c6d8195490565b6001840154604051611890929185916125de565b60405180910390a1610b636118a8638b78c6d8195490565b8360010154611e3c565b6118ba611bbe565b8181808060200260200160405190810160405280939291908181526020016000905b82821015611908576118f960408302860136819003810190612610565b815260200190600101906118dc565b5050835192505050600081900361194b576040517f7a3a4abc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806a52b7d2dcc80cd2e4000000815b84811015611a405785818151811061197657611976612596565b6020026020010151600001518461198d919061221d565b93508581815181106119a1576119a1612596565b602002602001015160200151836119b8919061221d565b9250818411156119f4576040517f97a4033a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710831115611a30576040517f01ef75fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a39816125ac565b905061195c565b50611a4d600e60006121b6565b8560005b81811015611abb57600e898983818110611a6d57611a6d612596565b835460018101855560009485526020909420604090910292909201926002029091019050611aa8828281358155602082013560018201555050565b505080611ab4906125ac565b9050611a51565b507faa4445051e370bfbe441733e8db78b143b18ab0869168167d6ed5d0f6a3a73508888604051611aed92919061265f565b60405180910390a15050505050505050565b611b07611bbe565b63389a75e1600c52806000526020600c208054421115611b2f57636f5e88186000526004601cfd5b60009055610b668161204a565b60606002600001805480602002602001604051908101604052809291908181526020018280548015611b8d57602002820191906000526020600020905b815481526020019060010190808311611b79575b5050505050905090565b611b9f611bbe565b8060601b611bb557637448fbae6000526004601cfd5b610b668161204a565b638b78c6d819543314611bd9576382b429006000526004601cfd5b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03828116600483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8391906126a6565b90506000611c9082612088565b6001600160a01b0385166000908152600f6020526040812054919250821215611d29576001600160a01b0385166000908152600f602090815260408083209290925581513381529081018390527f77dc5b08e57030bd992455124ec3a2ab396bebbb52d79f9a66ca969599beccfc910160405180910390a1601554611d1e906001600160a01b031682611e3c565b506000949350505050565b6001600160a01b0385166000908152601060205260409020548280821115611e3157600060026000018281548110611d6357611d63612596565b9060005260206000200154611d7783612109565b611d819190612561565b9050600060026000018481548110611d9b57611d9b612596565b9060005260206000200154611daf85612109565b611db99190612561565b90506000611dc78383612583565b9050611dd38187612583565b60408051338152602081018390529081018390529096507f0860b5ba07209bf03d5a245cc406668b636abc6c08063dee0137c8e5b60a8e849060600160405180910390a1601554611e2d906001600160a01b031682611e3c565b5050505b509095945050505050565b60008060008084865af16113485763b12d13eb6000526004601cfd5b600081611e6d670de0b6b3a76400008561254a565b611e779190612561565b90505b92915050565b60606000600d805480602002602001604051908101604052809291908181526020018280548015611ed057602002820191906000526020600020905b815481526020019060010190808311611ebc575b50505050509050600081519050600082600081518110611ef257611ef2612596565b602090810291909101015190506001805b83811015611f8d5782858281518110611f1e57611f1e612596565b60200260200101511115611f5157848181518110611f3e57611f3e612596565b6020026020010151925060019150611f7d565b82858281518110611f6457611f64612596565b602002602001015103611f7d57611f7a826125ac565b91505b611f86816125ac565b9050611f03565b5060008167ffffffffffffffff811115611fa957611fa9612280565b604051908082528060200260200182016040528015611fd2578160200160208202803683370190505b5090506000915060005b848110156120405783868281518110611ff757611ff7612596565b602002602001015103612030578082848151811061201757612017612596565b602090810291909101015261202d60018461221d565b92505b612039816125ac565b9050611fdc565b5095945050505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600e54600090815b818110156120f657600e81815481106120ab576120ab612596565b9060005260206000209060020201600001548410156120e65780156120da576120d56001826126bf565b6120de565b6000195b949350505050565b6120ef816125ac565b9050612090565b506121026001826126bf565b9392505050565b6000612710600e838154811061212157612121612596565b906000526020600020906002020160010154600560010154612143919061254a565b611e7a9190612561565b5080546000825590600052602060002090810190610b6691906121d7565b8280548282559060005260206000209081019282156121a6579160200282015b828111156121a657825182559160200191906001019061218b565b506121b29291506121d7565b5090565b5080546000825560020290600052602060002090810190610b6691906121ec565b5b808211156121b257600081556001016121d8565b5b808211156121b257600080825560018201556002016121ed565b634e487b7160e01b600052601160045260246000fd5b80820180821115611e7a57611e7a612207565b60006020828403121561224257600080fd5b5035919050565b80356001600160a01b038116811461226057600080fd5b919050565b60006020828403121561227757600080fd5b611e7782612249565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156122bf576122bf612280565b604052919050565b600067ffffffffffffffff8211156122e1576122e1612280565b5060051b60200190565b600060208083850312156122fe57600080fd5b823567ffffffffffffffff8082111561231657600080fd5b818501915085601f83011261232a57600080fd5b813561233d612338826122c7565b612296565b818152600591821b840185019185820191908984111561235c57600080fd5b8686015b848110156123ef578035868111156123785760008081fd5b8701603f81018c1361238a5760008081fd5b88810135604061239c612338836122c7565b82815291851b83018101918b8101908f8411156123b95760008081fd5b938201935b838510156123de576123cf85612249565b8252938c0193908c01906123be565b885250505093880193508701612360565b50909998505050505050505050565b60006020828403121561241057600080fd5b813560ff8116811461210257600080fd5b6020808252825160608383015280516080840181905260009291820190839060a08601905b808310156124665783518252928401926001929092019190840190612446565b50838701516040870152604087015160608701528094505050505092915050565b6000806020838503121561249a57600080fd5b823567ffffffffffffffff808211156124b257600080fd5b818501915085601f8301126124c657600080fd5b8135818111156124d557600080fd5b8660208260061b85010111156124ea57600080fd5b60209290920196919550909350505050565b600081518084526020808501945080840160005b8381101561252c57815187529582019590820190600101612510565b509495945050505050565b602081526000611e7760208301846124fc565b8082028115828204841417611e7a57611e7a612207565b60008261257e57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115611e7a57611e7a612207565b634e487b7160e01b600052603260045260246000fd5b6000600182016125be576125be612207565b5060010190565b60ff8281168282160390811115611e7a57611e7a612207565b6001600160a01b038416815260606020820152600061260060608301856124fc565b9050826040830152949350505050565b60006040828403121561262257600080fd5b6040516040810181811067ffffffffffffffff8211171561264557612645612280565b604052823581526020928301359281019290925250919050565b6020808252818101839052600090604080840186845b87811015612699578135835284820135858401529183019190830190600101612675565b5090979650505050505050565b6000602082840312156126b857600080fd5b5051919050565b81810360008312801583831316838312821617156126df576126df612207565b509291505056fea2646970667358221220409bc025ec0cf4d8138a33e9068576d66ffa47eabd787fdaa1e1bd1d570fd67a64736f6c63430008120033000000000000000000000000fb2c53db9140c96ef79341ef5574efb86fa5e90e

Deployed Bytecode

0x60806040526004361061021d5760003560e01c80638da5cb5b1161011d578063c8f74bb8116100b0578063f04e283e1161007f578063f2fde38b11610064578063f2fde38b1461060c578063f9b137d71461061f578063fee81cf4146106c357600080fd5b8063f04e283e146105d7578063f06d66ed146105ea57600080fd5b8063c8f74bb81461057c578063ca84bee0146105a9578063d65ab5f2146105bc578063daac7af7146105c457600080fd5b8063aabfec03116100ec578063aabfec0314610511578063b1f525c614610527578063b88a802f14610547578063c00007b01461055c57600080fd5b80638da5cb5b1461048d57806394a13541146104b9578063965fa21e146104d95780639711715a146104ef57600080fd5b806354d1f13d116101b0578063666da64f1161017f5780636cbc2ded116101645780636cbc2ded1461045057806370a0823114610458578063715018a61461048557600080fd5b8063666da64f146104285780636c6281981461043d57600080fd5b806354d1f13d146103a157806358e47004146103a95780635d841af5146103bc57806360b663bb146103cf57600080fd5b80631a9faa80116101ec5780631a9faa80146102e15780631aa62348146102f45780632569296214610321578063348b04f41461032957600080fd5b806302a251a314610241578063039af9eb1461026a578063085abff91461029f5780630f3a9f65146102cc57600080fd5b3661023c573460016000828254610234919061221d565b925050819055005b600080fd5b34801561024d57600080fd5b5061025760005481565b6040519081526020015b60405180910390f35b34801561027657600080fd5b5061028a610285366004612230565b6106f6565b60408051928352602083019190915201610261565b3480156102ab57600080fd5b506102576102ba366004612265565b60126020526000908152604090205481565b6102df6102da366004612230565b610724565b005b6102df6102ef366004612265565b61078c565b34801561030057600080fd5b5061025761030f366004612265565b60116020526000908152604090205481565b6102df61081f565b34801561033557600080fd5b50610377610344366004612230565b6013602052600090815260409020805460018201546002830154600384015460049094015460ff90931693919290919085565b6040805195151586526020860194909452928401919091526060830152608082015260a001610261565b6102df61086f565b6102df6103b7366004612265565b6108ab565b6102df6103ca366004612230565b610936565b3480156103db57600080fd5b506103e46109af565b6040516102619190600060a0820190508251151582526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b34801561043457600080fd5b506102df610a1d565b6102df61044b3660046122eb565b610b69565b6102df61105f565b34801561046457600080fd5b50610257610473366004612265565b600f6020526000908152604090205481565b6102df611149565b34801561049957600080fd5b50638b78c6d819546040516001600160a01b039091168152602001610261565b3480156104c557600080fd5b506102df6104d43660046123fe565b611183565b3480156104e557600080fd5b5061025760145481565b3480156104fb57600080fd5b5061050461134c565b6040516102619190612421565b34801561051d57600080fd5b5061025760015481565b34801561053357600080fd5b50610257610542366004612230565b6113e7565b34801561055357600080fd5b506102df611408565b34801561056857600080fd5b50610257610577366004612265565b6114eb565b34801561058857600080fd5b50610257610597366004612265565b60106020526000908152604090205481565b6102df6105b73660046123fe565b611578565b6102df611744565b6102df6105d2366004612487565b6118b2565b6102df6105e5366004612265565b611aff565b3480156105f657600080fd5b506105ff611b3c565b6040516102619190612537565b6102df61061a366004612265565b611b97565b34801561062b57600080fd5b5061068b604080516080810182526000808252602082018190529181018290526060810191909152506040805160808101825260055460ff8082161515835261010090910416602082015260065491810191909152600754606082015290565b604051610261919081511515815260208083015160ff1690820152604080830151908201526060918201519181019190915260800190565b3480156106cf57600080fd5b506102576106de366004612265565b63389a75e1600c908152600091909152602090205490565b600e818154811061070657600080fd5b60009182526020909120600290910201805460019091015490915082565b61072c611bbe565b60055460ff161561075057604051631b37a49b60e11b815260040160405180910390fd5b60008190556040518181527fc7f7e966c446ca07890210c0a7c9571d1d6f042fb01f27e51a921b2c3285de58906020015b60405180910390a150565b610794611bbe565b806001600160a01b0381166107bc5760405163d92e233d60e01b815260040160405180910390fd5b6016805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091556040519081527f74c630ccb1be1eeeb4a7b37a6042d7514309cb4db95249fcf21810e69b8e1f9b906020015b60405180910390a15050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6108b3611bbe565b806001600160a01b0381166108db5760405163d92e233d60e01b815260040160405180910390fd5b6015805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091556040519081527f1b95d297874c15a1a61f448b3d75e73df3e1db34cca1a51ea6a37e8f2afa5a8790602001610813565b61093e611bbe565b61271081111561097a576040517f8181adca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60148190556040518181527f5731b0070318c57b1e89e9f9b069ccc420b50d888b8c107793ecbde744b326c990602001610781565b6109e36040518060a00160405280600015158152602001600081526020016000815260200160008152602001600081525090565b506040805160a08101825260085460ff16151581526009546020820152600a5491810191909152600b546060820152600c54608082015290565b600754600590421080610a4057506000548160020154610a3d919061221d565b42115b15610a5e57604051632d0483c560e21b815260040160405180910390fd5b336000908152600f60205260408120549003610a8d5760405163374c934360e11b815260040160405180910390fd5b6000610a9833611bdb565b905080600003610aa6575050565b600061271060145483610ab9919061254a565b610ac39190612561565b9050610acf8183612583565b9150610ada336114eb565b610ae4908361221d565b336000818152600f6020908152604080832083905560118252808320929092558151928352820183905281018390529092507fa61ed5185dc0025e46a901a17312de0fd9453d957368006242930ed60391b4289060600160405180910390a1610b4d3383611e3c565b601554610b63906001600160a01b031682611e3c565b50505b50565b6016546001600160a01b03163314610bad576040517f02ca220300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460ff1615610bd157604051631b37a49b60e11b815260040160405180910390fd5b6007546004546002911015610c12576040517f78d1716100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600e805480602002602001604051908101604052809291908181526020016000905b82821015610c7c57838290600052602060002090600202016040518060400160405290816000820154815260200160018201548152505081526020019060010190610c36565b50508251929350610c929150829050600161221d565b845114610ce257610ca481600161221d565b6040517f0a2ab84c000000000000000000000000000000000000000000000000000000008152600401610cd991815260200190565b60405180910390fd5b60026000610cf0828261214d565b60018201600090556002820160009055505060008085600081518110610d1857610d18612596565b602002602001015151905060005b81811015610e0357600f600088600081518110610d4557610d45612596565b60200260200101518381518110610d5e57610d5e612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205483610d92919061221d565b92506000600f600089600081518110610dad57610dad612596565b60200260200101518481518110610dc657610dc6612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555080610dfc906125ac565b9050610d26565b50601554610e1a906001600160a01b031683611e3c565b85516040805160608101909152808567ffffffffffffffff811115610e4157610e41612280565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b50815260200160015481526020014281525060026000820151816000019080519060200190610e9a92919061216b565b5060208201516001808301919091556040909201516002909101555b81811015611050576000610ecb600183612583565b90506000898381518110610ee157610ee1612596565b602002602001015151905080896000018381548110610f0257610f02612596565b90600052602060002001819055506000816127108a8581518110610f2857610f28612596565b602002602001015160200151600154610f41919061254a565b610f4b9190612561565b610f559190612561565b905060005b8281101561103b5783601060008e8881518110610f7957610f79612596565b60200260200101518481518110610f9257610f92612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555081600f60008e8881518110610fd557610fd5612596565b60200260200101518481518110610fee57610fee612596565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254611025919061221d565b909155506110349050816125ac565b9050610f5a565b5050505080611049906125ac565b9050610eb6565b50506000600155505050505050565b611067611bbe565b60085460ff166110a2576040517ea3097100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60055460ff16156110c657604051631b37a49b60e11b815260040160405180910390fd5b6008805460ff19908116825534600b81815542600c908155600754600090815260136020908152604091829020805490961686556009546001870155600a54600287015592546003860155905460049094019390935591519081527fce24807f7e4b60b4e641462f13029fa5e5f79075bbc3f8e5cd43ecd19661d7609101610781565b611151611bbe565b6040517f0178ea2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007546005904210806111a6575060005481600201546111a3919061221d565b42115b156111c457604051632d0483c560e21b815260040160405180910390fd5b336000908152600f602052604081205490036111f35760405163374c934360e11b815260040160405180910390fd5b6005805460ff6101009091048116908416111561125857805461122090600190610100900460ff166125c5565b6040517f67a5681d00000000000000000000000000000000000000000000000000000000815260ff9091166004820152602401610cd9565b600061126333611bdb565b905061126e336114eb565b611278908261221d565b9050806000036112885750505050565b6001600d8560ff16815481106112a0576112a0612596565b9060005260206000200160008282546112b9919061221d565b9091555050336000818152600f602090815260408083208390556002860154601283529281902092909255815192835260ff8716908301527f76580ac7c171cd3d2029d56fd501d1351f2db92da148447b53227803bf940c02910160405180910390a18060086001016000828254611331919061221d565b909155505033600090815260116020526040902055505b5050565b61137060405180606001604052806060815260200160008152602001600081525090565b60408051600280546080602082028401810190945260608301818152929391928492909184918401828280156113c557602002820191906000526020600020905b8154815260200190600101908083116113b1575b5050505050815260200160018201548152602001600282015481525050905090565b600d81815481106113f757600080fd5b600091825260209091200154905081565b60055460ff161561142c57604051631b37a49b60e11b815260040160405180910390fd5b60085460ff16156114505760405163ad9a658360e01b815260040160405180910390fd5b600061145b336114eb565b905080158015611478575033600090815260116020526040902054155b156114965760405163374c934360e11b815260040160405180910390fd5b33600081815260116020908152604080832092909255815192835282018390527f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f7241910160405180910390a1610b663382611e3c565b6001600160a01b0381166000908152601160205260408120548082036115145750600092915050565b6001600160a01b038316600090815260126020908152604080832054835260139091528120600381015460018201549192916115509190611e58565b9050670de0b6b3a7640000611565828561254a565b61156f9190612561565b95945050505050565b638b78c6d819546001600160a01b0316336001600160a01b0316141580156115ab57506016546001600160a01b03163314155b156115c957604051636a7cb38760e01b815260040160405180910390fd5b60055460ff16156115ed57604051631b37a49b60e11b815260040160405180910390fd5b60028160ff16101561162b576040517f0db2f02f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160808082018352600180835260ff851660208085018290526003548587018190524260609687018190526005805461ffff1916610100860217909517909455600655600792909255845160a081018652600080825292810183905294850182905292840181905292018290526008805460ff191690556009829055600a829055600b829055600c9190915567ffffffffffffffff8111156116d2576116d2612280565b6040519080825280602002602001820160405280156116fb578160200160208202803683370190505b50805161171091600d9160209091019061216b565b5060405160ff821681527fbe58d0caad5cc482090345434dcddcd9ee5de5c8093d408bbbccec5ec3f259c590602001610781565b638b78c6d819546001600160a01b0316336001600160a01b03161415801561177757506016546001600160a01b03163314155b1561179557604051636a7cb38760e01b815260040160405180910390fd5b60085460ff16156117b95760405163ad9a658360e01b815260040160405180910390fd5b6000546007546005916008916117cf919061221d565b4210156117ef57604051632d0483c560e21b815260040160405180910390fd5b815460ff1661182a576040517fc878ebc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611834611e80565b835460ff199081168555835416600117835542600284015590507f8c2cde4db10afff6df05847c94cc85bd0b18815563d485e897585552dd14640461187c638b78c6d8195490565b6001840154604051611890929185916125de565b60405180910390a1610b636118a8638b78c6d8195490565b8360010154611e3c565b6118ba611bbe565b8181808060200260200160405190810160405280939291908181526020016000905b82821015611908576118f960408302860136819003810190612610565b815260200190600101906118dc565b5050835192505050600081900361194b576040517f7a3a4abc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806a52b7d2dcc80cd2e4000000815b84811015611a405785818151811061197657611976612596565b6020026020010151600001518461198d919061221d565b93508581815181106119a1576119a1612596565b602002602001015160200151836119b8919061221d565b9250818411156119f4576040517f97a4033a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710831115611a30576040517f01ef75fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a39816125ac565b905061195c565b50611a4d600e60006121b6565b8560005b81811015611abb57600e898983818110611a6d57611a6d612596565b835460018101855560009485526020909420604090910292909201926002029091019050611aa8828281358155602082013560018201555050565b505080611ab4906125ac565b9050611a51565b507faa4445051e370bfbe441733e8db78b143b18ab0869168167d6ed5d0f6a3a73508888604051611aed92919061265f565b60405180910390a15050505050505050565b611b07611bbe565b63389a75e1600c52806000526020600c208054421115611b2f57636f5e88186000526004601cfd5b60009055610b668161204a565b60606002600001805480602002602001604051908101604052809291908181526020018280548015611b8d57602002820191906000526020600020905b815481526020019060010190808311611b79575b5050505050905090565b611b9f611bbe565b8060601b611bb557637448fbae6000526004601cfd5b610b668161204a565b638b78c6d819543314611bd9576382b429006000526004601cfd5b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03828116600483015260009182917f000000000000000000000000fb2c53db9140c96ef79341ef5574efb86fa5e90e16906370a0823190602401602060405180830381865afa158015611c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8391906126a6565b90506000611c9082612088565b6001600160a01b0385166000908152600f6020526040812054919250821215611d29576001600160a01b0385166000908152600f602090815260408083209290925581513381529081018390527f77dc5b08e57030bd992455124ec3a2ab396bebbb52d79f9a66ca969599beccfc910160405180910390a1601554611d1e906001600160a01b031682611e3c565b506000949350505050565b6001600160a01b0385166000908152601060205260409020548280821115611e3157600060026000018281548110611d6357611d63612596565b9060005260206000200154611d7783612109565b611d819190612561565b9050600060026000018481548110611d9b57611d9b612596565b9060005260206000200154611daf85612109565b611db99190612561565b90506000611dc78383612583565b9050611dd38187612583565b60408051338152602081018390529081018390529096507f0860b5ba07209bf03d5a245cc406668b636abc6c08063dee0137c8e5b60a8e849060600160405180910390a1601554611e2d906001600160a01b031682611e3c565b5050505b509095945050505050565b60008060008084865af16113485763b12d13eb6000526004601cfd5b600081611e6d670de0b6b3a76400008561254a565b611e779190612561565b90505b92915050565b60606000600d805480602002602001604051908101604052809291908181526020018280548015611ed057602002820191906000526020600020905b815481526020019060010190808311611ebc575b50505050509050600081519050600082600081518110611ef257611ef2612596565b602090810291909101015190506001805b83811015611f8d5782858281518110611f1e57611f1e612596565b60200260200101511115611f5157848181518110611f3e57611f3e612596565b6020026020010151925060019150611f7d565b82858281518110611f6457611f64612596565b602002602001015103611f7d57611f7a826125ac565b91505b611f86816125ac565b9050611f03565b5060008167ffffffffffffffff811115611fa957611fa9612280565b604051908082528060200260200182016040528015611fd2578160200160208202803683370190505b5090506000915060005b848110156120405783868281518110611ff757611ff7612596565b602002602001015103612030578082848151811061201757612017612596565b602090810291909101015261202d60018461221d565b92505b612039816125ac565b9050611fdc565b5095945050505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b600e54600090815b818110156120f657600e81815481106120ab576120ab612596565b9060005260206000209060020201600001548410156120e65780156120da576120d56001826126bf565b6120de565b6000195b949350505050565b6120ef816125ac565b9050612090565b506121026001826126bf565b9392505050565b6000612710600e838154811061212157612121612596565b906000526020600020906002020160010154600560010154612143919061254a565b611e7a9190612561565b5080546000825590600052602060002090810190610b6691906121d7565b8280548282559060005260206000209081019282156121a6579160200282015b828111156121a657825182559160200191906001019061218b565b506121b29291506121d7565b5090565b5080546000825560020290600052602060002090810190610b6691906121ec565b5b808211156121b257600081556001016121d8565b5b808211156121b257600080825560018201556002016121ed565b634e487b7160e01b600052601160045260246000fd5b80820180821115611e7a57611e7a612207565b60006020828403121561224257600080fd5b5035919050565b80356001600160a01b038116811461226057600080fd5b919050565b60006020828403121561227757600080fd5b611e7782612249565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156122bf576122bf612280565b604052919050565b600067ffffffffffffffff8211156122e1576122e1612280565b5060051b60200190565b600060208083850312156122fe57600080fd5b823567ffffffffffffffff8082111561231657600080fd5b818501915085601f83011261232a57600080fd5b813561233d612338826122c7565b612296565b818152600591821b840185019185820191908984111561235c57600080fd5b8686015b848110156123ef578035868111156123785760008081fd5b8701603f81018c1361238a5760008081fd5b88810135604061239c612338836122c7565b82815291851b83018101918b8101908f8411156123b95760008081fd5b938201935b838510156123de576123cf85612249565b8252938c0193908c01906123be565b885250505093880193508701612360565b50909998505050505050505050565b60006020828403121561241057600080fd5b813560ff8116811461210257600080fd5b6020808252825160608383015280516080840181905260009291820190839060a08601905b808310156124665783518252928401926001929092019190840190612446565b50838701516040870152604087015160608701528094505050505092915050565b6000806020838503121561249a57600080fd5b823567ffffffffffffffff808211156124b257600080fd5b818501915085601f8301126124c657600080fd5b8135818111156124d557600080fd5b8660208260061b85010111156124ea57600080fd5b60209290920196919550909350505050565b600081518084526020808501945080840160005b8381101561252c57815187529582019590820190600101612510565b509495945050505050565b602081526000611e7760208301846124fc565b8082028115828204841417611e7a57611e7a612207565b60008261257e57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115611e7a57611e7a612207565b634e487b7160e01b600052603260045260246000fd5b6000600182016125be576125be612207565b5060010190565b60ff8281168282160390811115611e7a57611e7a612207565b6001600160a01b038416815260606020820152600061260060608301856124fc565b9050826040830152949350505050565b60006040828403121561262257600080fd5b6040516040810181811067ffffffffffffffff8211171561264557612645612280565b604052823581526020928301359281019290925250919050565b6020808252818101839052600090604080840186845b87811015612699578135835284820135858401529183019190830190600101612675565b5090979650505050505050565b6000602082840312156126b857600080fd5b5051919050565b81810360008312801583831316838312821617156126df576126df612207565b509291505056fea2646970667358221220409bc025ec0cf4d8138a33e9068576d66ffa47eabd787fdaa1e1bd1d570fd67a64736f6c63430008120033

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

000000000000000000000000fb2c53db9140c96ef79341ef5574efb86fa5e90e

-----Decoded View---------------
Arg [0] : _tokenAddress (address): 0xfb2c53db9140C96ef79341ef5574Efb86Fa5e90e

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000fb2c53db9140c96ef79341ef5574efb86fa5e90e


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  ]
[ 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.