ETH Price: $3,408.40 (+1.06%)

Contract

0x430D5DDE0Cd48989737a95B4C46e1801a9a08531
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Stake204585122024-08-04 23:34:59147 days ago1722814499IN
0x430D5DDE...1a9a08531
0 ETH0.00049541.99597388
Emergency Unstak...184535422023-10-29 4:44:11427 days ago1698554651IN
0x430D5DDE...1a9a08531
0 ETH0.008618568.93125263
Emergency Unstak...183124162023-10-09 10:47:59447 days ago1696848479IN
0x430D5DDE...1a9a08531
0 ETH0.006937327.42115398
Emergency Unstak...182903992023-10-06 8:53:59450 days ago1696582439IN
0x430D5DDE...1a9a08531
0 ETH0.005564176.53237387
Emergency Unstak...182903762023-10-06 8:49:23450 days ago1696582163IN
0x430D5DDE...1a9a08531
0 ETH0.000620965.64827221
Unstake181819922023-09-21 4:51:23465 days ago1695271883IN
0x430D5DDE...1a9a08531
0 ETH0.006160597.60032017
Stake181819802023-09-21 4:48:59465 days ago1695271739IN
0x430D5DDE...1a9a08531
0 ETH0.001681537.95561952
Emergency Unstak...181819602023-09-21 4:44:59465 days ago1695271499IN
0x430D5DDE...1a9a08531
0 ETH0.006428918.09717219
Unstake181723952023-09-19 20:39:23467 days ago1695155963IN
0x430D5DDE...1a9a08531
0 ETH0.0017789111.42844977
Stake181723842023-09-19 20:37:11467 days ago1695155831IN
0x430D5DDE...1a9a08531
0 ETH0.0020466112.83665865
Stake181718692023-09-19 18:53:59467 days ago1695149639IN
0x430D5DDE...1a9a08531
0 ETH0.0042082815.58031208
Emergency Unstak...179621632023-08-21 9:18:23496 days ago1692609503IN
0x430D5DDE...1a9a08531
0 ETH0.0106652312.92320616
Emergency Unstak...179541122023-08-20 6:15:59497 days ago1692512159IN
0x430D5DDE...1a9a08531
0 ETH0.0008661510.68626736
Emergency Unstak...179541012023-08-20 6:13:47497 days ago1692512027IN
0x430D5DDE...1a9a08531
0 ETH0.0010942612.14283621
Emergency Unstak...179344282023-08-17 12:05:59500 days ago1692273959IN
0x430D5DDE...1a9a08531
0 ETH0.0112700530.82854422
Emergency Unstak...179107082023-08-14 4:30:11503 days ago1691987411IN
0x430D5DDE...1a9a08531
0 ETH0.0119995912.30447438
Emergency Unstak...177010222023-07-15 20:05:23533 days ago1689451523IN
0x430D5DDE...1a9a08531
0 ETH0.0011261512.11011717
Emergency Unstak...176951312023-07-15 0:04:23534 days ago1689379463IN
0x430D5DDE...1a9a08531
0 ETH0.0209017815.31536655
Emergency Unstak...176617772023-07-10 7:26:23538 days ago1688973983IN
0x430D5DDE...1a9a08531
0 ETH0.0115723814.17869479
Emergency Unstak...176590272023-07-09 22:10:35539 days ago1688940635IN
0x430D5DDE...1a9a08531
0 ETH0.0010319212.73153128
Emergency Unstak...176589472023-07-09 21:54:35539 days ago1688939675IN
0x430D5DDE...1a9a08531
0 ETH0.054034314.36527884
Emergency Unstak...176077772023-07-02 17:21:59546 days ago1688318519IN
0x430D5DDE...1a9a08531
0 ETH0.0114237513.45047371
Emergency Unstak...175724882023-06-27 18:32:47551 days ago1687890767IN
0x430D5DDE...1a9a08531
0 ETH0.0115159813.51840233
Emergency Unstak...175684542023-06-27 4:51:59551 days ago1687841519IN
0x430D5DDE...1a9a08531
0 ETH0.0141596513.42718766
Emergency Unstak...175605032023-06-26 2:00:59552 days ago1687744859IN
0x430D5DDE...1a9a08531
0 ETH0.0159390711.65795605
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AceStaking

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 13 : AceStaking.sol
// SPDX-License-Identifier: MIT
// Creator: andreitoma8
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
import "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol";
import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol";
import "@openzeppelin/contracts/utils/introspection/ERC1820Implementer.sol";

contract AceStaking is
    Ownable,
    ReentrancyGuard,
    IERC721Receiver,
    IERC777Sender,
    IERC777Recipient,
    ERC1820Implementer
{
    // Interfaces for ERC777 and ERC721
    IERC777 public immutable rewardsToken;
    mapping(address => IERC721) public nftContracts;
    address[] public nftContractAddresses;

    // Reward Settings
    mapping(address => uint256) public rewardsForContract;
    mapping(address => mapping(uint256 => uint256))
        public additionalRewardsForToken;

    uint256 public rewardPeriodInSeconds;
    uint256 public totalTokensStakedCount;
    address internal REWARD_UPDATER;

    // Definition of Staker
    struct Staker {
        uint256 unclaimedRewards;
        uint256 lifetimeRewards;
        uint256 lastRewardedAt;
        uint256 lastClaimedAt;
    }

    // This is how we expect Tokens to be sent
    struct TokenData {
        address contractAddress;
        uint256 tokenIdentifier;
    }

    // How we keep track of a Staked token
    struct StakedToken {
        address contractAddress;
        uint256 tokenIdentifier;
        uint256 lastRewardedAt;
        uint256 stakedAt;
    }

    event UnstakedNFT(StakedToken stakedToken, address indexed staker);
    event StakedNFT(StakedToken stakedToken, address indexed staker);
    event RewardClaimed(address indexed staker, uint256 indexed amount);

    address[] public currentStakers;
    mapping(address => Staker) public stakers;
    mapping(address => StakedToken[]) public stakedTokensForAddress;
    mapping(address => mapping(uint256 => address)) public stakedTokenOwner;
    mapping(uint256 => mapping(address => uint256))
        internal sharesForWalletInRound;
    mapping(uint256 => address[]) internal walletsToRewardInRound;
    uint256 internal currentRoundId;

    uint256 public slashingPeriod = 24;

    // ERC777 Definitions
    event TokensToSendCalled(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes data,
        bytes operatorData,
        address token,
        uint256 fromBalance,
        uint256 toBalance
    );

    event TokensReceivedCalled(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes data,
        bytes operatorData,
        address token,
        uint256 fromBalance,
        uint256 toBalance
    );

    bool private _shouldRevertSend;
    bool private _shouldRevertReceive;

    IERC1820Registry internal constant _ERC1820_REGISTRY =
        IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
    bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH =
        keccak256("ERC777TokensSender");
    bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =
        keccak256("ERC777TokensRecipient");

    /**
     * @dev Setsup the new Contract.
     *
     * @param _rewardsToken A ERC777 Token. Can't be changed!
     * @param _rewardPeriodInSeconds Every x Seconds the Reward gets emitted. Can't be changed! Needs to be at least 60 Seconds but at least a day is recommneded.
     */
    constructor(IERC777 _rewardsToken, uint256 _rewardPeriodInSeconds) {
        require(
            _rewardPeriodInSeconds > 60,
            "AceStaking: Rewards need to be paid at least once per minute"
        );
        rewardsToken = _rewardsToken;
        rewardPeriodInSeconds = _rewardPeriodInSeconds;
        REWARD_UPDATER = msg.sender;
        _ERC1820_REGISTRY.setInterfaceImplementer(
            address(this),
            _TOKENS_RECIPIENT_INTERFACE_HASH,
            address(this)
        );
        _ERC1820_REGISTRY.setInterfaceImplementer(
            address(this),
            _TOKENS_SENDER_INTERFACE_HASH,
            address(this)
        );
    }

    /**
     * @dev Call this function to add a new NFT Contract to be accepted for staking.
     * Each contract can have differen rewards.
     *
     * Requirements: onlyOwner can add new Contracts; Contract needs to be ERC721 compliant.
     * @param _nftContract A ERC721 Contract that can be used for staking.
     * @param _reward ERC777 Token Value that is rewarded for each token every rewardPeriodInSeconds.
     */
    function addNFTContract(IERC721 _nftContract, uint256 _reward)
        public
        onlyOwner
    {
        nftContracts[address(_nftContract)] = _nftContract;
        nftContractAddresses.push(address(_nftContract));
        rewardsForContract[address(_nftContract)] = _reward;
    }

    /**
     * @dev Call this function to remove a NFT contract from beeing accepted for staking.
     * All tokens remain in the contract but wont receive any further rewards. They can be withdrawn by
     * the token owner.
     * Warning: Additional Rewards might stay in place, so call setAdditionalRewardsForTokens first and set their reward to 0.
     *
     * Requirements: onlyOwner can remove Contracts; Contract needs to be ERC721 compliant and already added through addNFTContract.
     * @param _nftContract A ERC721 Contract that should be removed from staking.
     */
    function removeNFTContract(address _nftContract) public onlyOwner {
        require(
            nftContracts[_nftContract] == IERC721(_nftContract),
            "AceStaking: Unkown Contract"
        );

        nftContracts[address(_nftContract)] = IERC721(address(0));
        rewardsForContract[address(_nftContract)] = 0;
        for (uint256 i; i < nftContractAddresses.length; i = unsafe_inc(i)) {
            if (nftContractAddresses[i] == _nftContract) {
                nftContractAddresses[i] = nftContractAddresses[
                    nftContractAddresses.length - 1
                ];
                nftContractAddresses.pop();
            }
        }
    }

    /**
     * @dev This function allows the contract owner to set an additional bonus that is
     * added for each token. The reward is added on top to the default reward for the contract.
     *
     * Requirements: onlyOwner or rewardUpdate (external contract) can remove Contracts; Contract needs to be ERC721 compliant
     * and already added through addNFTContract.
     * @param _nftContract A ERC721 Contract that is accepted by the contract.
     * @param _tokenIdentifiers Array of Identifiers that should receive the additional reward
     * @param _additionalReward ERC777 Token Value that is rewarded for each token every rewardPeriodInSeconds additionally to the default reward.
     */
    function setAdditionalRewardsForTokens(
        IERC721 _nftContract,
        uint256[] memory _tokenIdentifiers,
        uint256 _additionalReward
    ) external onlyRewardUpdater {
        require(
            nftContracts[address(_nftContract)] == IERC721(_nftContract),
            "AceStaking: Unkown Contract"
        );

        uint256 tokenCounter = _tokenIdentifiers.length;
        for (uint256 i; i < tokenCounter; i = unsafe_inc(i)) {
            additionalRewardsForToken[address(_nftContract)][
                _tokenIdentifiers[i]
            ] = _additionalReward;
        }
    }

    /**
     * @dev You need to claim your rewards at least once within this period.
     * If not you won't get any new rewards until you claim again.
     *
     * Requirements: onlyOwner can change that value
     *
     * @param _slashingPeriod Amount of Periods after that rewards get slashed
     */
    function setSlashingPeriod(uint256 _slashingPeriod) external onlyOwner {
        slashingPeriod = _slashingPeriod;
    }

    /**
     * @dev We reward a Bonus depending on different traits in some periods.
     * The choosen traits and to be rewareded tokens are calculated off-chain.
     * Tokens need to be staked when the reward is paid and already staked in the Snapshot of Tokens that is sent.
     * If you want to learn more about how our trait based bonus works take a look at our website.
     *
     * @param _tokens Array of TokenData(contractAddress, tokenId)
     * @param _totalBonus Amount of Tokens that should be distributed among sent tokens
     */
    function rewardBonus(TokenData[] calldata _tokens, uint256 _totalBonus)
        external
        onlyRewardUpdater
    {
        uint256 stakedTokensLength = _tokens.length;
        require(stakedTokensLength > 0, "AceStaking: No Tokens");
        require(
            _totalBonus > 0,
            "AceStaking: No Bonus to be distributed"
        );

        uint256 totalShares;
        currentRoundId += 1;
        for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
            address _staker = stakedTokenOwner[_tokens[i].contractAddress][
                _tokens[i].tokenIdentifier
            ];
            if (_staker != address(0)) {
                sharesForWalletInRound[currentRoundId][_staker] += 100;
                walletsToRewardInRound[currentRoundId].push(_staker);
                totalShares += 1;
            }
        }

        require(totalShares > 0, "AceStaking: No shares to distribute");

        uint256 walletsToRewardLength = walletsToRewardInRound[currentRoundId]
            .length;
        for (uint256 i; i < walletsToRewardLength; i = unsafe_inc(i)) {
            address walletToCheck = walletsToRewardInRound[currentRoundId][i];
            if (sharesForWalletInRound[currentRoundId][walletToCheck] > 0) {
                uint256 rewardsForWallet = (sharesForWalletInRound[
                    currentRoundId
                ][walletToCheck] / totalShares) * (_totalBonus / 100);

                stakers[walletToCheck].unclaimedRewards += rewardsForWallet;
                stakers[walletToCheck].lifetimeRewards += rewardsForWallet;

                sharesForWalletInRound[currentRoundId][walletToCheck] = 0;
            }
        }
    }

    /**
     * @dev Function to estimate rewards for specific token on a contract in one period.
     * Token ID could be out of range we don't care since this is just for
     * simulating unstaked token rewards for UI.
     *
     * Requirements: nftContractAddress needs to be registereed on the staking contract.
     * @param nftContractAddress A ERC721 Contract of the token
     * @param tokenIdentifier Token Identifier that you want an estimation for
     */
    function estimateRewardsForToken(
        address nftContractAddress,
        uint256 tokenIdentifier
    ) public view returns (uint256) {
        require(
            nftContracts[nftContractAddress] == IERC721(nftContractAddress),
            "AceStaking: Unkown Contract"
        );
        return rewardsForToken(nftContractAddress, tokenIdentifier);
    }

    /**
     * @dev Returns multiple stats for the address. Returns those values:
     * - totalTokensStaked: Count of Tokens for this Wallet on the Staking Contract
     * - unclaimedRewards: Rewards that can be claimed but are unclaimed by the user
     * - unaccountedRewards: Rewards that are not ready to be claimed
     * because the current period did not finish yet. If tokens were staked on different
     * start times this number might never be 0.
     * - lifetimeRewards: Just counting up what a user earned over life
     *
     * @param _stakerAddress Wallet Address that has staked tokens on the contract
     */
    function stakerStats(address _stakerAddress)
        public
        view
        returns (
            uint256 totalTokensStaked,
            uint256 unclaimedRewards,
            uint256 unaccountedRewards,
            uint256 lifetimeRewards
        )
    {
        Staker memory staker = stakers[_stakerAddress];
        uint256 claimableRewards = calculateUnaccountedRewards(
            _stakerAddress,
            false
        );
        return (
            stakedTokensForAddress[_stakerAddress].length,
            staker.unclaimedRewards + claimableRewards,
            calculateUnaccountedRewards(_stakerAddress, true),
            staker.lifetimeRewards + claimableRewards
        );
    }

    /**
     * @dev Function to unstake all tokens for the msg.sender.
     * Also rewards msg.sender for all of his staked tokens his staked tokens and ejects all tokens after this.
     * If you have many tokens staked (50+) we recommend unstaking them in badges to not hit the gas limit of a block.
     */
    function unstakeAllTokens() external nonReentrant {
        StakedToken[] memory stakedTokens = stakedTokensForAddress[msg.sender];
        uint256 stakedTokensLength = stakedTokens.length;
        require(stakedTokensLength > 0, "AceStaking: No Tokens found");
        rewardStaker(msg.sender);
        for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
            ejectToken(
                stakedTokens[i].contractAddress,
                stakedTokens[i].tokenIdentifier
            );
        }
    }

    /**
     * @dev Unstake a Set of Tokens for msg.sender.
     * Also rewards msg.sender for all of his staked tokens his staked tokens and ejects all sent tokens after this.
     *
     * @param _tokens Array of TokenData(contractAddress, tokenId)
     */
    function unstake(TokenData[] calldata _tokens) external nonReentrant {
        uint256 stakedTokensLength = _tokens.length;
        require(stakedTokensLength > 0, "AceStaking: No Tokens found");
        rewardStaker(msg.sender);
        for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
            ejectToken(_tokens[i].contractAddress, _tokens[i].tokenIdentifier);
        }
    }

    /**
     * @dev Emergency Unstake Function: Unstake without any reward calculation.
     * !!! NOT RECOMMENDED, YOU MIGHT LOSE UNACCOUNTED REWARDS !!!
     * When to use? This function consumes less gas then the normal unstake since we do not reward all tokens before unstaking.
     * In case you hit the block limit for gas (very unlikely) we have a way to withdrawal your tokens somehow.
     * @param _tokens Array of TokenData(contractAddress, tokenId)
     */
    function emergencyUnstake(TokenData[] calldata _tokens)
        external
        nonReentrant
    {
        uint256 stakedTokensLength = _tokens.length;
        require(stakedTokensLength > 0, "AceStaking: No Tokens in calldata");
        for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
            ejectToken(_tokens[i].contractAddress, _tokens[i].tokenIdentifier);
        }
    }

    /**
     * @dev This function transfers tokens to the contract with transferFrom.
     * thusfor we need to call addToken manually but we save a little on gas.
     *
     * Requirements: All token contracts need to be added to this contract and be approved by the user.
     *
     * @param _tokens Array of TokenData(contractAddress, tokenId)
     */
    function stake(TokenData[] calldata _tokens) external {
        beforeTokensAdded(msg.sender);
        uint256 tokensLength = _tokens.length;
        for (uint256 i; i < tokensLength; i = unsafe_inc(i)) {
            IERC721(_tokens[i].contractAddress).transferFrom(
                msg.sender,
                address(this),
                _tokens[i].tokenIdentifier
            );
            addToken(
                _tokens[i].contractAddress,
                _tokens[i].tokenIdentifier,
                msg.sender
            );
        }
    }

    /**
     * @dev Call this function to get your ERC777 Token rewards transfered to your wallet.
     * This is an expensive call since we calculate your current earnings and send them in one
     * Transaction to your wallet.
     *
     */
    function claimRewards() external nonReentrant {
        rewardStaker(msg.sender);
        require(
            stakers[msg.sender].unclaimedRewards > 0,
            "AceStaking: Nothing to claim"
        );
        IERC777(rewardsToken).send(
            msg.sender,
            stakers[msg.sender].unclaimedRewards,
            ""
        );
        emit RewardClaimed(msg.sender, stakers[msg.sender].unclaimedRewards);
        stakers[msg.sender].unclaimedRewards = 0;
        stakers[msg.sender].lastClaimedAt = block.timestamp;
    }

    /**
     * @dev This function determains how many periods should be calculated for rewarding.
     *
     * @param _lastRewardedAt timestamp when the token was rewarded last
     * @param _lastClaimedAt timestamp when ist was last claimed
     *
     */
    function rewardPeriods(uint256 _lastRewardedAt, uint256 _lastClaimedAt)
        internal
        view
        returns (uint256 _rewardPeriodCounter)
    {
        uint256 referenceTimestamp = block.timestamp;
        if (
            referenceTimestamp >
            (_lastClaimedAt + slashingPeriod * rewardPeriodInSeconds)
        ) {
            referenceTimestamp =
                _lastClaimedAt +
                slashingPeriod *
                rewardPeriodInSeconds;
        }
        return (referenceTimestamp - _lastRewardedAt) / rewardPeriodInSeconds;
    }

    /**
     * @dev Calculates Rewards for a User and accounts them to his entry.
     *
     * @param _stakerAddress staker that should be rewarded
     *
     */
    function rewardUnaccountedRewards(address _stakerAddress)
        internal
        returns (uint256 _rewards)
    {
        StakedToken[] memory stakedTokens = stakedTokensForAddress[
            _stakerAddress
        ];
        uint256 totalRewards;
        uint256 stakedTokensCount = stakedTokens.length;
        for (uint256 i; i < stakedTokensCount; i = unsafe_inc(i)) {
            uint256 periodsToReward = rewardPeriods(
                stakedTokens[i].lastRewardedAt,
                stakers[_stakerAddress].lastClaimedAt
            );

            if (periodsToReward > 0) {
                totalRewards +=
                    periodsToReward *
                    rewardsForToken(
                        stakedTokens[i].contractAddress,
                        stakedTokens[i].tokenIdentifier
                    );
                if (periodsToReward == slashingPeriod) {
                    stakedTokensForAddress[_stakerAddress][i].lastRewardedAt =
                        stakedTokensForAddress[_stakerAddress][i].stakedAt +
                        (
                            uint256(
                                (block.timestamp -
                                    stakedTokensForAddress[_stakerAddress][i]
                                        .stakedAt) / rewardPeriodInSeconds
                            )
                        ) *
                        rewardPeriodInSeconds;
                } else {
                    stakedTokensForAddress[_stakerAddress][i].lastRewardedAt =
                        stakedTokensForAddress[_stakerAddress][i].stakedAt +
                        periodsToReward *
                        rewardPeriodInSeconds;
                }
            }
        }
        return totalRewards;
    }

    /**
     * @dev Calculates Rewards for a User but does not account them.
     *
     * @param _stakerAddress staker that should be rewarded
     * @param _simulateUnaccounted include unaccounted rewards that can't be claimed yet
     *
     */
    function calculateUnaccountedRewards(
        address _stakerAddress,
        bool _simulateUnaccounted
    ) internal view returns (uint256 _rewards) {
        StakedToken[] memory stakedTokens = stakedTokensForAddress[
            _stakerAddress
        ];
        uint256 totalRewards;
        uint256 stakedTokensCount = stakedTokens.length;
        for (uint256 i; i < stakedTokensCount; i = unsafe_inc(i)) {
            uint256 periodsToReward = rewardPeriods(
                stakedTokens[i].lastRewardedAt,
                stakers[_stakerAddress].lastClaimedAt
            );

            uint256 tokenReward = rewardsForToken(
                stakedTokens[i].contractAddress,
                stakedTokens[i].tokenIdentifier
            );
            if (_simulateUnaccounted) {
                totalRewards +=
                    ((((block.timestamp - stakedTokens[i].lastRewardedAt) *
                        100) / rewardPeriodInSeconds) * tokenReward) /
                    100 -
                    periodsToReward *
                    tokenReward;
            } else {
                totalRewards += tokenReward * periodsToReward;
            }
        }
        return totalRewards;
    }

    /**
     * @dev Summarize rewards for a specific token on a contract.
     * Sums up default rewards for contract and bonus for token identifier.
     *
     * @param nftContractAddress Contract to check
     * @param tokenIdentifier Token Identifier to check
     *
     */
    function rewardsForToken(
        address nftContractAddress,
        uint256 tokenIdentifier
    ) internal view returns (uint256) {
        return
            rewardsForContract[nftContractAddress] +
            additionalRewardsForToken[nftContractAddress][tokenIdentifier];
    }

    /**
     * @dev Function that moves all unaccounted rewards to unclaimed.
     * This call is required to keep our internal balance sheets up to date.
     * Depending on Token Amount this is very expensive to call since we loop through all tokens!
     *
     * @param _address Wallet Address that should be rewarded
     *
     */
    function rewardStaker(address _address) internal {
        uint256 unaccountedRewards = rewardUnaccountedRewards(_address);
        stakers[_address].lastRewardedAt = block.timestamp;
        stakers[_address].unclaimedRewards += unaccountedRewards;
        stakers[_address].lifetimeRewards += unaccountedRewards;
    }

    /**
     * @dev Internal function to send a token back to a user. Also
     * removes / updates all contract internal trackings.
     *
     * Requirements: msg.sender needs to be the wallet that sent the token to the contract.
     *
     * @param nftContractAddress Contract for the token to be ejected
     * @param tokenIdentifier Token Identifier for the token to be ejected
     *
     */
    function ejectToken(address nftContractAddress, uint256 tokenIdentifier)
        internal
    {
        require(
            stakedTokenOwner[nftContractAddress][tokenIdentifier] == msg.sender,
            "AceStaking: Not your token..."
        );

        IERC721(nftContractAddress).transferFrom(
            address(this),
            msg.sender,
            tokenIdentifier
        );

        for (
            uint256 i;
            i < stakedTokensForAddress[msg.sender].length;
            i = unsafe_inc(i)
        ) {
            if (
                stakedTokensForAddress[msg.sender][i].tokenIdentifier ==
                tokenIdentifier &&
                stakedTokensForAddress[msg.sender][i].contractAddress ==
                nftContractAddress
            ) {
                emit UnstakedNFT(
                    stakedTokensForAddress[msg.sender][i],
                    msg.sender
                );
                stakedTokensForAddress[msg.sender][i] = stakedTokensForAddress[
                    msg.sender
                ][stakedTokensForAddress[msg.sender].length - 1];
                stakedTokensForAddress[msg.sender].pop();
            }
        }

        if (stakedTokensForAddress[msg.sender].length == 0) {
            for (uint256 i; i < currentStakers.length; i = unsafe_inc(i)) {
                if (currentStakers[i] == msg.sender) {
                    currentStakers[i] = currentStakers[
                        currentStakers.length - 1
                    ];
                    currentStakers.pop();
                }
            }
        }

        stakedTokenOwner[msg.sender][tokenIdentifier] = address(0);
        totalTokensStakedCount -= 1;
    }

    /**
     * @dev Helper that should be called before any token is added. Needs to be called
     * only once per batch. It basically setup the staker object.
     *
     * @param _staker Wallet Address for Staker
     */
    function beforeTokensAdded(address _staker) internal {
        if (stakedTokensForAddress[_staker].length == 0) {
            if (stakers[_staker].lastRewardedAt > 0) {
                // This wallet already staked before and was at least rewarded once.
                stakers[_staker].lastRewardedAt = block.timestamp;
                stakers[_staker].lastClaimedAt = block.timestamp;
            } else {
                // This wallet is new to us
                stakers[_staker] = Staker(
                    stakers[_staker].unclaimedRewards,
                    stakers[_staker].lifetimeRewards,
                    block.timestamp,
                    block.timestamp
                );
            }
            currentStakers.push(_staker);
        }
    }

    /**
     * @dev Function to add a token and regiter it in all mappings that we need to
     * return and reward a token.
     *
     * @param nftContractAddress Contract of the token
     * @param tokenIdentifier The Identifier of a token
     * @param tokenOwnerAddress The address of the current owner
     */
    function addToken(
        address nftContractAddress,
        uint256 tokenIdentifier,
        address tokenOwnerAddress
    ) internal {
        require(
            nftContracts[nftContractAddress] == IERC721(nftContractAddress),
            "AceStaking: Unkown Contract"
        );

        StakedToken memory newToken = StakedToken(
            nftContractAddress,
            tokenIdentifier,
            block.timestamp,
            block.timestamp
        );
        stakedTokenOwner[nftContractAddress][
            tokenIdentifier
        ] = tokenOwnerAddress;

        stakedTokensForAddress[tokenOwnerAddress].push(newToken);
        totalTokensStakedCount += 1;
        emit StakedNFT(newToken, tokenOwnerAddress);
    }

    function unsafe_inc(uint256 x) private pure returns (uint256) {
        unchecked {
            return x + 1;
        }
    }

    /**
     * @dev See {IERC721Receiver-onERC721Received}. Also registers token in our TokenRegistry.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address, // operator not required
        address tokenOwnerAddress,
        uint256 tokenIdentifier,
        bytes memory
    ) public virtual override returns (bytes4) {
        beforeTokensAdded(tokenOwnerAddress);
        addToken(msg.sender, tokenIdentifier, tokenOwnerAddress);
        return this.onERC721Received.selector;
    }

    function tokensToSend(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external override {
        if (_shouldRevertSend) {
            revert();
        }

        IERC777 token = IERC777(_msgSender());

        uint256 fromBalance = token.balanceOf(from);
        // when called due to burn, to will be the zero address, which will have a balance of 0
        uint256 toBalance = token.balanceOf(to);

        emit TokensToSendCalled(
            operator,
            from,
            to,
            amount,
            userData,
            operatorData,
            address(token),
            fromBalance,
            toBalance
        );
    }

    function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external override {
        if (_shouldRevertReceive) {
            revert();
        }

        IERC777 token = IERC777(_msgSender());

        uint256 fromBalance = token.balanceOf(from);
        // when called due to burn, to will be the zero address, which will have a balance of 0
        uint256 toBalance = token.balanceOf(to);

        emit TokensReceivedCalled(
            operator,
            from,
            to,
            amount,
            userData,
            operatorData,
            address(token),
            fromBalance,
            toBalance
        );
    }


    /**
     * @dev This address is allowed to change the rewards for a specific token.
     * Since opening a chest door results in a different reward, this is implemented in the chest door opener contract later.
     *
     * @param _REWARD_UPDATER Address that is allowed to modify rewards
     */
    function setRewardUpdater(address _REWARD_UPDATER) external onlyOwner {
        REWARD_UPDATER = _REWARD_UPDATER;
    }

    modifier onlyRewardUpdater() {
        require(
            msg.sender == REWARD_UPDATER || msg.sender == owner(),
            "AceStaking: Only REWARD_UPDATE or OWNER."
        );
        _;
    }
}

/** created with bowline.app **/

File 2 of 13 : IERC1820Registry.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC1820Registry.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the global ERC1820 Registry, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
 * implementers for interfaces in this registry, as well as query support.
 *
 * Implementers may be shared by multiple accounts, and can also implement more
 * than a single interface for each account. Contracts can implement interfaces
 * for themselves, but externally-owned accounts (EOA) must delegate this to a
 * contract.
 *
 * {IERC165} interfaces can also be queried via the registry.
 *
 * For an in-depth explanation and source code analysis, see the EIP text.
 */
interface IERC1820Registry {
    /**
     * @dev Sets `newManager` as the manager for `account`. A manager of an
     * account is able to set interface implementers for it.
     *
     * By default, each account is its own manager. Passing a value of `0x0` in
     * `newManager` will reset the manager to this initial state.
     *
     * Emits a {ManagerChanged} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     */
    function setManager(address account, address newManager) external;

    /**
     * @dev Returns the manager for `account`.
     *
     * See {setManager}.
     */
    function getManager(address account) external view returns (address);

    /**
     * @dev Sets the `implementer` contract as ``account``'s implementer for
     * `interfaceHash`.
     *
     * `account` being the zero address is an alias for the caller's address.
     * The zero address can also be used in `implementer` to remove an old one.
     *
     * See {interfaceHash} to learn how these are created.
     *
     * Emits an {InterfaceImplementerSet} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
     * end in 28 zeroes).
     * - `implementer` must implement {IERC1820Implementer} and return true when
     * queried for support, unless `implementer` is the caller. See
     * {IERC1820Implementer-canImplementInterfaceForAddress}.
     */
    function setInterfaceImplementer(
        address account,
        bytes32 _interfaceHash,
        address implementer
    ) external;

    /**
     * @dev Returns the implementer of `interfaceHash` for `account`. If no such
     * implementer is registered, returns the zero address.
     *
     * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
     * zeroes), `account` will be queried for support of it.
     *
     * `account` being the zero address is an alias for the caller's address.
     */
    function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);

    /**
     * @dev Returns the interface hash for an `interfaceName`, as defined in the
     * corresponding
     * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
     */
    function interfaceHash(string calldata interfaceName) external pure returns (bytes32);

    /**
     * @notice Updates the cache with whether the contract implements an ERC165 interface or not.
     * @param account Address of the contract for which to update the cache.
     * @param interfaceId ERC165 interface for which to update the cache.
     */
    function updateERC165Cache(address account, bytes4 interfaceId) external;

    /**
     * @notice Checks whether a contract implements an ERC165 interface or not.
     * If the result is not cached a direct lookup on the contract address is performed.
     * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
     * {updateERC165Cache} with the contract address.
     * @param account Address of the contract to check.
     * @param interfaceId ERC165 interface to check.
     * @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);

    /**
     * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
     * @param account Address of the contract to check.
     * @param interfaceId ERC165 interface to check.
     * @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);

    event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);

    event ManagerChanged(address indexed account, address indexed newManager);
}

File 3 of 13 : IERC1820Implementer.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC1820Implementer.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface for an ERC1820 implementer, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP].
 * Used by contracts that will be registered as implementers in the
 * {IERC1820Registry}.
 */
interface IERC1820Implementer {
    /**
     * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract
     * implements `interfaceHash` for `account`.
     *
     * See {IERC1820Registry-setInterfaceImplementer}.
     */
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32);
}

File 4 of 13 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

File 5 of 13 : ERC1820Implementer.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/introspection/ERC1820Implementer.sol)

pragma solidity ^0.8.0;

import "./IERC1820Implementer.sol";

/**
 * @dev Implementation of the {IERC1820Implementer} interface.
 *
 * Contracts may inherit from this and call {_registerInterfaceForAddress} to
 * declare their willingness to be implementers.
 * {IERC1820Registry-setInterfaceImplementer} should then be called for the
 * registration to be complete.
 */
contract ERC1820Implementer is IERC1820Implementer {
    bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");

    mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces;

    /**
     * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}.
     */
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address account)
        public
        view
        virtual
        override
        returns (bytes32)
    {
        return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00);
    }

    /**
     * @dev Declares the contract as willing to be an implementer of
     * `interfaceHash` for `account`.
     *
     * See {IERC1820Registry-setInterfaceImplementer} and
     * {IERC1820Registry-interfaceHash}.
     */
    function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual {
        _supportedInterfaces[interfaceHash][account] = true;
    }
}

File 6 of 13 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 7 of 13 : IERC777Sender.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777Sender.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC777TokensSender standard as defined in the EIP.
 *
 * {IERC777} Token holders can be notified of operations performed on their
 * tokens by having a contract implement this interface (contract holders can be
 * their own implementer) and registering it on the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
 *
 * See {IERC1820Registry} and {ERC1820Implementer}.
 */
interface IERC777Sender {
    /**
     * @dev Called by an {IERC777} token contract whenever a registered holder's
     * (`from`) tokens are about to be moved or destroyed. The type of operation
     * is conveyed by `to` being the zero address or not.
     *
     * This call occurs _before_ the token contract's state is updated, so
     * {IERC777-balanceOf}, etc., can be used to query the pre-operation state.
     *
     * This function may revert to prevent the operation from being executed.
     */
    function tokensToSend(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external;
}

File 8 of 13 : IERC777Recipient.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777Recipient.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
 *
 * Accounts can be notified of {IERC777} tokens being sent to them by having a
 * contract implement this interface (contract holders can be their own
 * implementer) and registering it on the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
 *
 * See {IERC1820Registry} and {ERC1820Implementer}.
 */
interface IERC777Recipient {
    /**
     * @dev Called by an {IERC777} token contract whenever tokens are being
     * moved or created into a registered account (`to`). The type of operation
     * is conveyed by `from` being the zero address or not.
     *
     * This call occurs _after_ the token contract's state is updated, so
     * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
     *
     * This function may revert to prevent the operation from being executed.
     */
    function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external;
}

File 9 of 13 : IERC777.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC777/IERC777.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC777Token standard as defined in the EIP.
 *
 * This contract uses the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
 * token holders and recipients react to token movements by using setting implementers
 * for the associated interfaces in said registry. See {IERC1820Registry} and
 * {ERC1820Implementer}.
 */
interface IERC777 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the smallest part of the token that is not divisible. This
     * means all token operations (creation, movement and destruction) must have
     * amounts that are a multiple of this number.
     *
     * For most token contracts, this value will equal 1.
     */
    function granularity() external view returns (uint256);

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

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * If send or receive hooks are registered for the caller and `recipient`,
     * the corresponding functions will be called with `data` and empty
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function send(
        address recipient,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev Destroys `amount` tokens from the caller's account, reducing the
     * total supply.
     *
     * If a send hook is registered for the caller, the corresponding function
     * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     */
    function burn(uint256 amount, bytes calldata data) external;

    /**
     * @dev Returns true if an account is an operator of `tokenHolder`.
     * Operators can send and burn tokens on behalf of their owners. All
     * accounts are their own operator.
     *
     * See {operatorSend} and {operatorBurn}.
     */
    function isOperatorFor(address operator, address tokenHolder) external view returns (bool);

    /**
     * @dev Make an account an operator of the caller.
     *
     * See {isOperatorFor}.
     *
     * Emits an {AuthorizedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function authorizeOperator(address operator) external;

    /**
     * @dev Revoke an account's operator status for the caller.
     *
     * See {isOperatorFor} and {defaultOperators}.
     *
     * Emits a {RevokedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function revokeOperator(address operator) external;

    /**
     * @dev Returns the list of default operators. These accounts are operators
     * for all token holders, even if {authorizeOperator} was never called on
     * them.
     *
     * This list is immutable, but individual holders may revoke these via
     * {revokeOperator}, in which case {isOperatorFor} will return false.
     */
    function defaultOperators() external view returns (address[] memory);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
     * be an operator of `sender`.
     *
     * If send or receive hooks are registered for `sender` and `recipient`,
     * the corresponding functions will be called with `data` and
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - `sender` cannot be the zero address.
     * - `sender` must have at least `amount` tokens.
     * - the caller must be an operator for `sender`.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function operatorSend(
        address sender,
        address recipient,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the total supply.
     * The caller must be an operator of `account`.
     *
     * If a send hook is registered for `account`, the corresponding function
     * will be called with `data` and `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     * - the caller must be an operator for `account`.
     */
    function operatorBurn(
        address account,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );

    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);

    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);

    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

File 10 of 13 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 11 of 13 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

File 12 of 13 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 13 of 13 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "evmVersion": "london",
  "libraries": {},
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC777","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_rewardPeriodInSeconds","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"},{"internalType":"uint256","name":"lastRewardedAt","type":"uint256"},{"internalType":"uint256","name":"stakedAt","type":"uint256"}],"indexed":false,"internalType":"struct AceStaking.StakedToken","name":"stakedToken","type":"tuple"},{"indexed":true,"internalType":"address","name":"staker","type":"address"}],"name":"StakedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"operatorData","type":"bytes"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toBalance","type":"uint256"}],"name":"TokensReceivedCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"operatorData","type":"bytes"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toBalance","type":"uint256"}],"name":"TokensToSendCalled","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"},{"internalType":"uint256","name":"lastRewardedAt","type":"uint256"},{"internalType":"uint256","name":"stakedAt","type":"uint256"}],"indexed":false,"internalType":"struct AceStaking.StakedToken","name":"stakedToken","type":"tuple"},{"indexed":true,"internalType":"address","name":"staker","type":"address"}],"name":"UnstakedNFT","type":"event"},{"inputs":[{"internalType":"contract IERC721","name":"_nftContract","type":"address"},{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"addNFTContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"additionalRewardsForToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"interfaceHash","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"canImplementInterfaceForAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"currentStakers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"}],"internalType":"struct AceStaking.TokenData[]","name":"_tokens","type":"tuple[]"}],"name":"emergencyUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"}],"name":"estimateRewardsForToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftContractAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nftContracts","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"tokenOwnerAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_nftContract","type":"address"}],"name":"removeNFTContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"}],"internalType":"struct AceStaking.TokenData[]","name":"_tokens","type":"tuple[]"},{"internalType":"uint256","name":"_totalBonus","type":"uint256"}],"name":"rewardBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardPeriodInSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardsForContract","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract IERC777","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"_nftContract","type":"address"},{"internalType":"uint256[]","name":"_tokenIdentifiers","type":"uint256[]"},{"internalType":"uint256","name":"_additionalReward","type":"uint256"}],"name":"setAdditionalRewardsForTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_REWARD_UPDATER","type":"address"}],"name":"setRewardUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slashingPeriod","type":"uint256"}],"name":"setSlashingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slashingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"}],"internalType":"struct AceStaking.TokenData[]","name":"_tokens","type":"tuple[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedTokenOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedTokensForAddress","outputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"},{"internalType":"uint256","name":"lastRewardedAt","type":"uint256"},{"internalType":"uint256","name":"stakedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_stakerAddress","type":"address"}],"name":"stakerStats","outputs":[{"internalType":"uint256","name":"totalTokensStaked","type":"uint256"},{"internalType":"uint256","name":"unclaimedRewards","type":"uint256"},{"internalType":"uint256","name":"unaccountedRewards","type":"uint256"},{"internalType":"uint256","name":"lifetimeRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakers","outputs":[{"internalType":"uint256","name":"unclaimedRewards","type":"uint256"},{"internalType":"uint256","name":"lifetimeRewards","type":"uint256"},{"internalType":"uint256","name":"lastRewardedAt","type":"uint256"},{"internalType":"uint256","name":"lastClaimedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"},{"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"tokensReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"},{"internalType":"bytes","name":"operatorData","type":"bytes"}],"name":"tokensToSend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalTokensStakedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenIdentifier","type":"uint256"}],"internalType":"struct AceStaking.TokenData[]","name":"_tokens","type":"tuple[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakeAllTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405260186011553480156200001657600080fd5b506040516200586e3803806200586e83398181016040528101906200003c919062000400565b6200005c620000506200027b60201b60201c565b6200028360201b60201c565b60018081905550603c8111620000a9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000a090620004ce565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250508060078190555033600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550731820a4b7618bde71dce8cdc73aab6c95905fad2473ffffffffffffffffffffffffffffffffffffffff166329965a1d307fb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b306040518463ffffffff1660e01b815260040162000198939291906200051c565b600060405180830381600087803b158015620001b357600080fd5b505af1158015620001c8573d6000803e3d6000fd5b50505050731820a4b7618bde71dce8cdc73aab6c95905fad2473ffffffffffffffffffffffffffffffffffffffff166329965a1d307f29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895306040518463ffffffff1660e01b81526004016200023f939291906200051c565b600060405180830381600087803b1580156200025a57600080fd5b505af11580156200026f573d6000803e3d6000fd5b50505050505062000559565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000379826200034c565b9050919050565b60006200038d826200036c565b9050919050565b6200039f8162000380565b8114620003ab57600080fd5b50565b600081519050620003bf8162000394565b92915050565b6000819050919050565b620003da81620003c5565b8114620003e657600080fd5b50565b600081519050620003fa81620003cf565b92915050565b600080604083850312156200041a576200041962000347565b5b60006200042a85828601620003ae565b92505060206200043d85828601620003e9565b9150509250929050565b600082825260208201905092915050565b7f4163655374616b696e673a2052657761726473206e65656420746f206265207060008201527f616964206174206c65617374206f6e636520706572206d696e75746500000000602082015250565b6000620004b6603c8362000447565b9150620004c38262000458565b604082019050919050565b60006020820190508181036000830152620004e981620004a7565b9050919050565b620004fb816200036c565b82525050565b6000819050919050565b620005168162000501565b82525050565b6000606082019050620005336000830186620004f0565b6200054260208301856200050b565b620005516040830184620004f0565b949350505050565b6080516152f26200057c60003960008181610adc0152611b0201526152f26000f3fe608060405234801561001057600080fd5b50600436106101ef5760003560e01c8063889dc3051161010f578063d1af0c7d116100a2578063dce9f7aa11610071578063dce9f7aa146105cf578063ed24f661146105eb578063f2fde38b14610609578063ffac2ae314610625576101ef565b8063d1af0c7d1461055b578063d5dbbce514610579578063d64f152a14610595578063da1af08f146105b1576101ef565b806394d8d7e9116100de57806394d8d7e9146104be578063a65ae961146104da578063ada0f98f1461050d578063bf91d8e41461053d576101ef565b8063889dc305146104355780638da5cb5b146104515780638fc67c261461046f5780639168ae721461048b576101ef565b8063385bfbb411610187578063715018a611610156578063715018a6146103c357806375ab9782146103cd5780637dad54c8146103e9578063801c523614610419576101ef565b8063385bfbb41461033a5780634258fcf614610356578063499d9d94146103895780635d85f3a1146103b9576101ef565b806318b85e33116101c357806318b85e33146102a05780631bbd8a41146102d0578063249cb3fa14610300578063372500ab14610330576101ef565b806223de29146101f45780630c4735b814610210578063145778d614610240578063150b7a0214610270575b600080fd5b61020e60048036038101906102099190613f53565b610641565b005b61022a60048036038101906102259190614022565b6107bb565b6040516102379190614071565b60405180910390f35b61025a6004803603810190610255919061408c565b61089c565b60405161026791906140c8565b60405180910390f35b61028a60048036038101906102859190614224565b6108db565b60405161029791906142e2565b60405180910390f35b6102ba60048036038101906102b5919061408c565b610903565b6040516102c791906140c8565b60405180910390f35b6102ea60048036038101906102e59190614022565b610942565b6040516102f79190614071565b60405180910390f35b61031a60048036038101906103159190614333565b610967565b6040516103279190614382565b60405180910390f35b6103386109fe565b005b610354600480360381019061034f919061439d565b610cc7565b005b610370600480360381019061036b919061439d565b61105e565b60405161038094939291906143ca565b60405180910390f35b6103a3600480360381019061039e919061439d565b611168565b6040516103b09190614071565b60405180910390f35b6103c1611180565b005b6103cb61139a565b005b6103e760048036038101906103e29190613f53565b611422565b005b61040360048036038101906103fe9190614022565b61159c565b60405161041091906140c8565b60405180910390f35b610433600480360381019061042e9190614465565b6115de565b005b61044f600480360381019061044a9190614465565b611716565b005b610459611827565b60405161046691906140c8565b60405180910390f35b6104896004803603810190610484919061439d565b611850565b005b6104a560048036038101906104a0919061439d565b611910565b6040516104b594939291906143ca565b60405180910390f35b6104d860048036038101906104d39190614465565b611940565b005b6104f460048036038101906104ef9190614022565b611a5a565b60405161050494939291906144b2565b60405180910390f35b6105276004803603810190610522919061439d565b611ac7565b6040516105349190614556565b60405180910390f35b610545611afa565b6040516105529190614071565b60405180910390f35b610563611b00565b6040516105709190614592565b60405180910390f35b610593600480360381019061058e91906146ae565b611b24565b005b6105af60048036038101906105aa919061408c565b611d55565b005b6105b9611ddb565b6040516105c69190614071565b60405180910390f35b6105e960048036038101906105e4919061471d565b611de1565b005b6105f3611f86565b6040516106009190614071565b60405180910390f35b610623600480360381019061061e919061439d565b611f8c565b005b61063f600480360381019061063a919061475d565b612084565b005b601260019054906101000a900460ff161561065b57600080fd5b60006106656126b2565b905060008173ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016106a291906140c8565b602060405180830381865afa1580156106bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e391906147d2565b905060008273ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161072091906140c8565b602060405180830381865afa15801561073d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076191906147d2565b90507f47e915878c47f3ec4d7ff646a2becb229f64fd2abe4d2b5e2bb4275b0cf50d4e8b8b8b8b8b8b8b8b8b8b8b6040516107a69b9a9998979695949392919061483d565b60405180910390a15050505050505050505050565b60008273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461088a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088190614939565b60405180910390fd5b61089483836126ba565b905092915050565b600481815481106108ac57600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006108e68461275f565b6108f13384866129ff565b63150b7a0260e01b9050949350505050565b600a818154811061091357600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6006602052816000526040600020602052806000526040600020600091509150505481565b60006002600084815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff166109d4576000801b6109f6565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b905092915050565b60026001541415610a44576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3b906149a5565b60405180910390fd5b6002600181905550610a5533612cd4565b6000600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015411610ada576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad190614a11565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639bd9bbc633600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001546040518363ffffffff1660e01b8152600401610b77929190614a57565b600060405180830381600087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b50505050600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001543373ffffffffffffffffffffffffffffffffffffffff167f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f724160405160405180910390a36000600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555042600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018190555060018081905550565b610ccf6126b2565b73ffffffffffffffffffffffffffffffffffffffff16610ced611827565b73ffffffffffffffffffffffffffffffffffffffff1614610d43576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3a90614adf565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610e10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0790614939565b60405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060005b60048054905081101561105a578173ffffffffffffffffffffffffffffffffffffffff1660048281548110610f0f57610f0e614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561104a5760046001600480549050610f6a9190614b5d565b81548110610f7b57610f7a614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660048281548110610fba57610fb9614aff565b5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600480548061101457611013614b91565b5b6001900381819060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905590555b61105381612dde565b9050610ed7565b5050565b6000806000806000600b60008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050905060006110e7876000612deb565b9050600c60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905081836000015161113c9190614bc0565b611147896001612deb565b8385602001516111579190614bc0565b955095509550955050509193509193565b60056020528060005260406000206000915090505481565b600260015414156111c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111bd906149a5565b60405180910390fd5b60026001819055506000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156112d557838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160018201548152602001600282015481526020016003820154815250508152602001906001019061122f565b50505050905060008151905060008111611324576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161131b90614c62565b60405180910390fd5b61132d33612cd4565b60005b8181101561138e5761137e83828151811061134e5761134d614aff565b5b60200260200101516000015184838151811061136d5761136c614aff565b5b602002602001015160200151613084565b61138781612dde565b9050611330565b50505060018081905550565b6113a26126b2565b73ffffffffffffffffffffffffffffffffffffffff166113c0611827565b73ffffffffffffffffffffffffffffffffffffffff1614611416576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161140d90614adf565b60405180910390fd5b61142060006138bb565b565b601260009054906101000a900460ff161561143c57600080fd5b60006114466126b2565b905060008173ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161148391906140c8565b602060405180830381865afa1580156114a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c491906147d2565b905060008273ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161150191906140c8565b602060405180830381865afa15801561151e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154291906147d2565b90507faa3e88aca472e90221daf7d3d601abafb62b120319089d7a2c2f63588da855298b8b8b8b8b8b8b8b8b8b8b6040516115879b9a9998979695949392919061483d565b60405180910390a15050505050505050505050565b600d6020528160005260406000206020528060005260406000206000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6115e73361275f565b600082829050905060005b818110156117105783838281811061160d5761160c614aff565b5b9050604002016000016020810190611625919061439d565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd333087878681811061165557611654614aff565b5b905060400201602001356040518463ffffffff1660e01b815260040161167d93929190614c82565b600060405180830381600087803b15801561169757600080fd5b505af11580156116ab573d6000803e3d6000fd5b505050506117008484838181106116c5576116c4614aff565b5b90506040020160000160208101906116dd919061439d565b8585848181106116f0576116ef614aff565b5b90506040020160200135336129ff565b61170981612dde565b90506115f2565b50505050565b6002600154141561175c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611753906149a5565b60405180910390fd5b60026001819055506000828290509050600081116117af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a690614d2b565b60405180910390fd5b60005b8181101561181a5761180a8484838181106117d0576117cf614aff565b5b90506040020160000160208101906117e8919061439d565b8585848181106117fb576117fa614aff565b5b90506040020160200135613084565b61181381612dde565b90506117b2565b5050600180819055505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6118586126b2565b73ffffffffffffffffffffffffffffffffffffffff16611876611827565b73ffffffffffffffffffffffffffffffffffffffff16146118cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118c390614adf565b60405180910390fd5b80600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b6020528060005260406000206000915090508060000154908060010154908060020154908060030154905084565b60026001541415611986576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197d906149a5565b60405180910390fd5b60026001819055506000828290509050600081116119d9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119d090614c62565b60405180910390fd5b6119e233612cd4565b60005b81811015611a4d57611a3d848483818110611a0357611a02614aff565b5b9050604002016000016020810190611a1b919061439d565b858584818110611a2e57611a2d614aff565b5b90506040020160200135613084565b611a4681612dde565b90506119e5565b5050600180819055505050565b600c6020528160005260406000208181548110611a7657600080fd5b9060005260206000209060040201600091509150508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010154908060020154908060030154905084565b60036020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60075481565b7f000000000000000000000000000000000000000000000000000000000000000081565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480611bb25750611b83611827565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b611bf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be890614dbd565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611cbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cb590614939565b60405180910390fd5b60008251905060005b81811015611d4e5782600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868481518110611d2457611d23614aff565b5b6020026020010151815260200190815260200160002081905550611d4781612dde565b9050611cc7565b5050505050565b611d5d6126b2565b73ffffffffffffffffffffffffffffffffffffffff16611d7b611827565b73ffffffffffffffffffffffffffffffffffffffff1614611dd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dc890614adf565b60405180910390fd5b8060118190555050565b60085481565b611de96126b2565b73ffffffffffffffffffffffffffffffffffffffff16611e07611827565b73ffffffffffffffffffffffffffffffffffffffff1614611e5d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e5490614adf565b60405180910390fd5b81600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506004829080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505050565b60115481565b611f946126b2565b73ffffffffffffffffffffffffffffffffffffffff16611fb2611827565b73ffffffffffffffffffffffffffffffffffffffff1614612008576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fff90614adf565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612078576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161206f90614e4f565b60405180910390fd5b612081816138bb565b50565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061211257506120e3611827565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612151576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161214890614dbd565b60405180910390fd5b60008383905090506000811161219c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161219390614ebb565b60405180910390fd5b600082116121df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121d690614f4d565b60405180910390fd5b60006001601060008282546121f49190614bc0565b9250508190555060005b828110156123f6576000600d600088888581811061221f5761221e614aff565b5b9050604002016000016020810190612237919061439d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600088888581811061228657612285614aff565b5b90506040020160200135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146123e5576064600e6000601054815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546123589190614bc0565b92505081905550600f60006010548152602001908152602001600020819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001836123e29190614bc0565b92505b506123ef81612dde565b90506121fe565b506000811161243a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161243190614fdf565b60405180910390fd5b6000600f6000601054815260200190815260200160002080549050905060005b818110156126a9576000600f60006010548152602001908152602001600020828154811061248b5761248a614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600e6000601054815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115612698576000606487612522919061502e565b85600e6000601054815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612580919061502e565b61258a919061505f565b905080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282546125de9190614bc0565b9250508190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008282546126379190614bc0565b925050819055506000600e6000601054815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b506126a281612dde565b905061245a565b50505050505050565b600033905090565b6000600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546127579190614bc0565b905092915050565b6000600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905014156129fc576000600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002015411156128885742600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018190555042600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030181905550612998565b6040518060800160405280600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001548152602001600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154815260200142815260200142815250600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301559050505b600a819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b8273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612acc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ac390614939565b60405180910390fd5b600060405180608001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200184815260200142815260200142815250905081600d60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015560408201518160020155606082015181600301555050600160086000828254612c799190614bc0565b925050819055508173ffffffffffffffffffffffffffffffffffffffff167fa5c6423d7b62a11b9a8a75349fbfc9865029364ea11b6ba86dabdaf8a406529d82604051612cc6919061512c565b60405180910390a250505050565b6000612cdf8261397f565b905042600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000828254612d7a9190614bc0565b9250508190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000828254612dd39190614bc0565b925050819055505050565b6000600182019050919050565b600080600c60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015612ef357838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015481526020016002820154815260200160038201548152505081526020019060010190612e4d565b5050505090506000808251905060005b81811015613077576000612f77858381518110612f2357612f22614aff565b5b602002602001015160400151600b60008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030154613dde565b90506000612fc1868481518110612f9157612f90614aff565b5b602002602001015160000151878581518110612fb057612faf614aff565b5b6020026020010151602001516126ba565b9050871561304b578082612fd5919061505f565b60648260075460648a8881518110612ff057612fef614aff565b5b602002602001015160400151426130079190614b5d565b613011919061505f565b61301b919061502e565b613025919061505f565b61302f919061502e565b6130399190614b5d565b856130449190614bc0565b9450613065565b8181613057919061505f565b856130629190614bc0565b94505b505061307081612dde565b9050612f03565b5081935050505092915050565b3373ffffffffffffffffffffffffffffffffffffffff16600d60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613162576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161315990615193565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166323b872dd3033846040518463ffffffff1660e01b815260040161319f93929190614c82565b600060405180830381600087803b1580156131b957600080fd5b505af11580156131cd573d6000803e3d6000fd5b5050505060005b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490508110156136395781600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106132705761326f614aff565b5b90600052602060002090600402016001015414801561333c57508273ffffffffffffffffffffffffffffffffffffffff16600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106132f2576132f1614aff565b5b906000526020600020906004020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15613629573373ffffffffffffffffffffffffffffffffffffffff167fe4303adcf299b488ab61917b621f0c76d6e78519f605a66909dcfe7f0167753b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002083815481106133ca576133c9614aff565b5b90600052602060002090600402016040516133e591906152a1565b60405180910390a2600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061347b9190614b5d565b8154811061348c5761348b614aff565b5b9060005260206000209060040201600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106134eb576134ea614aff565b5b90600052602060002090600402016000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600182015481600101556002820154816002015560038201548160030155905050600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806135d0576135cf614b91565b5b6001900381819060005260206000209060040201600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201600090556003820160009055505090555b61363281612dde565b90506131d4565b506000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080549050141561380d5760005b600a8054905081101561380b573373ffffffffffffffffffffffffffffffffffffffff16600a82815481106136c0576136bf614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156137fb57600a6001600a8054905061371b9190614b5d565b8154811061372c5761372b614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600a828154811061376b5761376a614aff565b5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600a8054806137c5576137c4614b91565b5b6001900381819060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905590555b61380481612dde565b9050613688565b505b6000600d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600860008282546138b09190614b5d565b925050819055505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015613a8757838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820154815260200160028201548152602001600382015481525050815260200190600101906139e1565b5050505090506000808251905060005b81811015613dd2576000613b0b858381518110613ab757613ab6614aff565b5b602002602001015160400151600b60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030154613dde565b90506000811115613dc157613b5c858381518110613b2c57613b2b614aff565b5b602002602001015160000151868481518110613b4b57613b4a614aff565b5b6020026020010151602001516126ba565b81613b67919061505f565b84613b729190614bc0565b9350601154811415613cde57600754600754600c60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208481548110613bd557613bd4614aff565b5b90600052602060002090600402016003015442613bf29190614b5d565b613bfc919061502e565b613c06919061505f565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613c5757613c56614aff565b5b906000526020600020906004020160030154613c739190614bc0565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613cc457613cc3614aff565b5b906000526020600020906004020160020181905550613dc0565b60075481613cec919061505f565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613d3d57613d3c614aff565b5b906000526020600020906004020160030154613d599190614bc0565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613daa57613da9614aff565b5b9060005260206000209060040201600201819055505b5b50613dcb81612dde565b9050613a97565b50819350505050919050565b600080429050600754601154613df4919061505f565b83613dff9190614bc0565b811115613e2457600754601154613e16919061505f565b83613e219190614bc0565b90505b6007548482613e339190614b5d565b613e3d919061502e565b91505092915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613e8582613e5a565b9050919050565b613e9581613e7a565b8114613ea057600080fd5b50565b600081359050613eb281613e8c565b92915050565b6000819050919050565b613ecb81613eb8565b8114613ed657600080fd5b50565b600081359050613ee881613ec2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112613f1357613f12613eee565b5b8235905067ffffffffffffffff811115613f3057613f2f613ef3565b5b602083019150836001820283011115613f4c57613f4b613ef8565b5b9250929050565b60008060008060008060008060c0898b031215613f7357613f72613e50565b5b6000613f818b828c01613ea3565b9850506020613f928b828c01613ea3565b9750506040613fa38b828c01613ea3565b9650506060613fb48b828c01613ed9565b955050608089013567ffffffffffffffff811115613fd557613fd4613e55565b5b613fe18b828c01613efd565b945094505060a089013567ffffffffffffffff81111561400457614003613e55565b5b6140108b828c01613efd565b92509250509295985092959890939650565b6000806040838503121561403957614038613e50565b5b600061404785828601613ea3565b925050602061405885828601613ed9565b9150509250929050565b61406b81613eb8565b82525050565b60006020820190506140866000830184614062565b92915050565b6000602082840312156140a2576140a1613e50565b5b60006140b084828501613ed9565b91505092915050565b6140c281613e7a565b82525050565b60006020820190506140dd60008301846140b9565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614131826140e8565b810181811067ffffffffffffffff821117156141505761414f6140f9565b5b80604052505050565b6000614163613e46565b905061416f8282614128565b919050565b600067ffffffffffffffff82111561418f5761418e6140f9565b5b614198826140e8565b9050602081019050919050565b82818337600083830152505050565b60006141c76141c284614174565b614159565b9050828152602081018484840111156141e3576141e26140e3565b5b6141ee8482856141a5565b509392505050565b600082601f83011261420b5761420a613eee565b5b813561421b8482602086016141b4565b91505092915050565b6000806000806080858703121561423e5761423d613e50565b5b600061424c87828801613ea3565b945050602061425d87828801613ea3565b935050604061426e87828801613ed9565b925050606085013567ffffffffffffffff81111561428f5761428e613e55565b5b61429b878288016141f6565b91505092959194509250565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6142dc816142a7565b82525050565b60006020820190506142f760008301846142d3565b92915050565b6000819050919050565b614310816142fd565b811461431b57600080fd5b50565b60008135905061432d81614307565b92915050565b6000806040838503121561434a57614349613e50565b5b60006143588582860161431e565b925050602061436985828601613ea3565b9150509250929050565b61437c816142fd565b82525050565b60006020820190506143976000830184614373565b92915050565b6000602082840312156143b3576143b2613e50565b5b60006143c184828501613ea3565b91505092915050565b60006080820190506143df6000830187614062565b6143ec6020830186614062565b6143f96040830185614062565b6144066060830184614062565b95945050505050565b60008083601f84011261442557614424613eee565b5b8235905067ffffffffffffffff81111561444257614441613ef3565b5b60208301915083604082028301111561445e5761445d613ef8565b5b9250929050565b6000806020838503121561447c5761447b613e50565b5b600083013567ffffffffffffffff81111561449a57614499613e55565b5b6144a68582860161440f565b92509250509250929050565b60006080820190506144c760008301876140b9565b6144d46020830186614062565b6144e16040830185614062565b6144ee6060830184614062565b95945050505050565b6000819050919050565b600061451c61451761451284613e5a565b6144f7565b613e5a565b9050919050565b600061452e82614501565b9050919050565b600061454082614523565b9050919050565b61455081614535565b82525050565b600060208201905061456b6000830184614547565b92915050565b600061457c82614523565b9050919050565b61458c81614571565b82525050565b60006020820190506145a76000830184614583565b92915050565b60006145b882613e7a565b9050919050565b6145c8816145ad565b81146145d357600080fd5b50565b6000813590506145e5816145bf565b92915050565b600067ffffffffffffffff821115614606576146056140f9565b5b602082029050602081019050919050565b600061462a614625846145eb565b614159565b9050808382526020820190506020840283018581111561464d5761464c613ef8565b5b835b8181101561467657806146628882613ed9565b84526020840193505060208101905061464f565b5050509392505050565b600082601f83011261469557614694613eee565b5b81356146a5848260208601614617565b91505092915050565b6000806000606084860312156146c7576146c6613e50565b5b60006146d5868287016145d6565b935050602084013567ffffffffffffffff8111156146f6576146f5613e55565b5b61470286828701614680565b925050604061471386828701613ed9565b9150509250925092565b6000806040838503121561473457614733613e50565b5b6000614742858286016145d6565b925050602061475385828601613ed9565b9150509250929050565b60008060006040848603121561477657614775613e50565b5b600084013567ffffffffffffffff81111561479457614793613e55565b5b6147a08682870161440f565b935093505060206147b386828701613ed9565b9150509250925092565b6000815190506147cc81613ec2565b92915050565b6000602082840312156147e8576147e7613e50565b5b60006147f6848285016147bd565b91505092915050565b600082825260208201905092915050565b600061481c83856147ff565b93506148298385846141a5565b614832836140e8565b840190509392505050565b600061012082019050614853600083018e6140b9565b614860602083018d6140b9565b61486d604083018c6140b9565b61487a606083018b614062565b818103608083015261488d81898b614810565b905081810360a08301526148a2818789614810565b90506148b160c08301866140b9565b6148be60e0830185614062565b6148cc610100830184614062565b9c9b505050505050505050505050565b600082825260208201905092915050565b7f4163655374616b696e673a20556e6b6f776e20436f6e74726163740000000000600082015250565b6000614923601b836148dc565b915061492e826148ed565b602082019050919050565b6000602082019050818103600083015261495281614916565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b600061498f601f836148dc565b915061499a82614959565b602082019050919050565b600060208201905081810360008301526149be81614982565b9050919050565b7f4163655374616b696e673a204e6f7468696e6720746f20636c61696d00000000600082015250565b60006149fb601c836148dc565b9150614a06826149c5565b602082019050919050565b60006020820190508181036000830152614a2a816149ee565b9050919050565b50565b6000614a416000836147ff565b9150614a4c82614a31565b600082019050919050565b6000606082019050614a6c60008301856140b9565b614a796020830184614062565b8181036040830152614a8a81614a34565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000614ac96020836148dc565b9150614ad482614a93565b602082019050919050565b60006020820190508181036000830152614af881614abc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614b6882613eb8565b9150614b7383613eb8565b925082821015614b8657614b85614b2e565b5b828203905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000614bcb82613eb8565b9150614bd683613eb8565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614c0b57614c0a614b2e565b5b828201905092915050565b7f4163655374616b696e673a204e6f20546f6b656e7320666f756e640000000000600082015250565b6000614c4c601b836148dc565b9150614c5782614c16565b602082019050919050565b60006020820190508181036000830152614c7b81614c3f565b9050919050565b6000606082019050614c9760008301866140b9565b614ca460208301856140b9565b614cb16040830184614062565b949350505050565b7f4163655374616b696e673a204e6f20546f6b656e7320696e2063616c6c64617460008201527f6100000000000000000000000000000000000000000000000000000000000000602082015250565b6000614d156021836148dc565b9150614d2082614cb9565b604082019050919050565b60006020820190508181036000830152614d4481614d08565b9050919050565b7f4163655374616b696e673a204f6e6c79205245574152445f555044415445206f60008201527f72204f574e45522e000000000000000000000000000000000000000000000000602082015250565b6000614da76028836148dc565b9150614db282614d4b565b604082019050919050565b60006020820190508181036000830152614dd681614d9a565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614e396026836148dc565b9150614e4482614ddd565b604082019050919050565b60006020820190508181036000830152614e6881614e2c565b9050919050565b7f4163655374616b696e673a204e6f20546f6b656e730000000000000000000000600082015250565b6000614ea56015836148dc565b9150614eb082614e6f565b602082019050919050565b60006020820190508181036000830152614ed481614e98565b9050919050565b7f4163655374616b696e673a204e6f20426f6e757320746f20626520646973747260008201527f6962757465640000000000000000000000000000000000000000000000000000602082015250565b6000614f376026836148dc565b9150614f4282614edb565b604082019050919050565b60006020820190508181036000830152614f6681614f2a565b9050919050565b7f4163655374616b696e673a204e6f2073686172657320746f206469737472696260008201527f7574650000000000000000000000000000000000000000000000000000000000602082015250565b6000614fc96023836148dc565b9150614fd482614f6d565b604082019050919050565b60006020820190508181036000830152614ff881614fbc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061503982613eb8565b915061504483613eb8565b92508261505457615053614fff565b5b828204905092915050565b600061506a82613eb8565b915061507583613eb8565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156150ae576150ad614b2e565b5b828202905092915050565b6150c281613e7a565b82525050565b6150d181613eb8565b82525050565b6080820160008201516150ed60008501826150b9565b50602082015161510060208501826150c8565b50604082015161511360408501826150c8565b50606082015161512660608501826150c8565b50505050565b600060808201905061514160008301846150d7565b92915050565b7f4163655374616b696e673a204e6f7420796f757220746f6b656e2e2e2e000000600082015250565b600061517d601d836148dc565b915061518882615147565b602082019050919050565b600060208201905081810360008301526151ac81615170565b9050919050565b60008160001c9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006151f36151ee836151b3565b6151c0565b9050919050565b6000819050919050565b6000615217615212836151b3565b6151fa565b9050919050565b608082016000808301549050615233816151e0565b61524060008601826150b9565b506001830154905061525181615204565b61525e60208601826150c8565b506002830154905061526f81615204565b61527c60408601826150c8565b506003830154905061528d81615204565b61529a60608601826150c8565b5050505050565b60006080820190506152b6600083018461521e565b9291505056fea264697066735822122014cddb4bbe1d87685a22bd4ea40cc27813315a23ff054040b5b74e94b147c5a864736f6c634300080a0033000000000000000000000000917f7a44f93d76d70e254c96a2c7077375b369f10000000000000000000000000000000000000000000000000000000000278d00

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101ef5760003560e01c8063889dc3051161010f578063d1af0c7d116100a2578063dce9f7aa11610071578063dce9f7aa146105cf578063ed24f661146105eb578063f2fde38b14610609578063ffac2ae314610625576101ef565b8063d1af0c7d1461055b578063d5dbbce514610579578063d64f152a14610595578063da1af08f146105b1576101ef565b806394d8d7e9116100de57806394d8d7e9146104be578063a65ae961146104da578063ada0f98f1461050d578063bf91d8e41461053d576101ef565b8063889dc305146104355780638da5cb5b146104515780638fc67c261461046f5780639168ae721461048b576101ef565b8063385bfbb411610187578063715018a611610156578063715018a6146103c357806375ab9782146103cd5780637dad54c8146103e9578063801c523614610419576101ef565b8063385bfbb41461033a5780634258fcf614610356578063499d9d94146103895780635d85f3a1146103b9576101ef565b806318b85e33116101c357806318b85e33146102a05780631bbd8a41146102d0578063249cb3fa14610300578063372500ab14610330576101ef565b806223de29146101f45780630c4735b814610210578063145778d614610240578063150b7a0214610270575b600080fd5b61020e60048036038101906102099190613f53565b610641565b005b61022a60048036038101906102259190614022565b6107bb565b6040516102379190614071565b60405180910390f35b61025a6004803603810190610255919061408c565b61089c565b60405161026791906140c8565b60405180910390f35b61028a60048036038101906102859190614224565b6108db565b60405161029791906142e2565b60405180910390f35b6102ba60048036038101906102b5919061408c565b610903565b6040516102c791906140c8565b60405180910390f35b6102ea60048036038101906102e59190614022565b610942565b6040516102f79190614071565b60405180910390f35b61031a60048036038101906103159190614333565b610967565b6040516103279190614382565b60405180910390f35b6103386109fe565b005b610354600480360381019061034f919061439d565b610cc7565b005b610370600480360381019061036b919061439d565b61105e565b60405161038094939291906143ca565b60405180910390f35b6103a3600480360381019061039e919061439d565b611168565b6040516103b09190614071565b60405180910390f35b6103c1611180565b005b6103cb61139a565b005b6103e760048036038101906103e29190613f53565b611422565b005b61040360048036038101906103fe9190614022565b61159c565b60405161041091906140c8565b60405180910390f35b610433600480360381019061042e9190614465565b6115de565b005b61044f600480360381019061044a9190614465565b611716565b005b610459611827565b60405161046691906140c8565b60405180910390f35b6104896004803603810190610484919061439d565b611850565b005b6104a560048036038101906104a0919061439d565b611910565b6040516104b594939291906143ca565b60405180910390f35b6104d860048036038101906104d39190614465565b611940565b005b6104f460048036038101906104ef9190614022565b611a5a565b60405161050494939291906144b2565b60405180910390f35b6105276004803603810190610522919061439d565b611ac7565b6040516105349190614556565b60405180910390f35b610545611afa565b6040516105529190614071565b60405180910390f35b610563611b00565b6040516105709190614592565b60405180910390f35b610593600480360381019061058e91906146ae565b611b24565b005b6105af60048036038101906105aa919061408c565b611d55565b005b6105b9611ddb565b6040516105c69190614071565b60405180910390f35b6105e960048036038101906105e4919061471d565b611de1565b005b6105f3611f86565b6040516106009190614071565b60405180910390f35b610623600480360381019061061e919061439d565b611f8c565b005b61063f600480360381019061063a919061475d565b612084565b005b601260019054906101000a900460ff161561065b57600080fd5b60006106656126b2565b905060008173ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016106a291906140c8565b602060405180830381865afa1580156106bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e391906147d2565b905060008273ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161072091906140c8565b602060405180830381865afa15801561073d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076191906147d2565b90507f47e915878c47f3ec4d7ff646a2becb229f64fd2abe4d2b5e2bb4275b0cf50d4e8b8b8b8b8b8b8b8b8b8b8b6040516107a69b9a9998979695949392919061483d565b60405180910390a15050505050505050505050565b60008273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461088a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161088190614939565b60405180910390fd5b61089483836126ba565b905092915050565b600481815481106108ac57600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006108e68461275f565b6108f13384866129ff565b63150b7a0260e01b9050949350505050565b600a818154811061091357600080fd5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6006602052816000526040600020602052806000526040600020600091509150505481565b60006002600084815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff166109d4576000801b6109f6565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b905092915050565b60026001541415610a44576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3b906149a5565b60405180910390fd5b6002600181905550610a5533612cd4565b6000600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000015411610ada576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad190614a11565b60405180910390fd5b7f000000000000000000000000917f7a44f93d76d70e254c96a2c7077375b369f173ffffffffffffffffffffffffffffffffffffffff16639bd9bbc633600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001546040518363ffffffff1660e01b8152600401610b77929190614a57565b600060405180830381600087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b50505050600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001543373ffffffffffffffffffffffffffffffffffffffff167f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f724160405160405180910390a36000600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018190555042600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018190555060018081905550565b610ccf6126b2565b73ffffffffffffffffffffffffffffffffffffffff16610ced611827565b73ffffffffffffffffffffffffffffffffffffffff1614610d43576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3a90614adf565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610e10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0790614939565b60405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060005b60048054905081101561105a578173ffffffffffffffffffffffffffffffffffffffff1660048281548110610f0f57610f0e614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561104a5760046001600480549050610f6a9190614b5d565b81548110610f7b57610f7a614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660048281548110610fba57610fb9614aff565b5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600480548061101457611013614b91565b5b6001900381819060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905590555b61105381612dde565b9050610ed7565b5050565b6000806000806000600b60008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051806080016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050905060006110e7876000612deb565b9050600c60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905081836000015161113c9190614bc0565b611147896001612deb565b8385602001516111579190614bc0565b955095509550955050509193509193565b60056020528060005260406000206000915090505481565b600260015414156111c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111bd906149a5565b60405180910390fd5b60026001819055506000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156112d557838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160018201548152602001600282015481526020016003820154815250508152602001906001019061122f565b50505050905060008151905060008111611324576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161131b90614c62565b60405180910390fd5b61132d33612cd4565b60005b8181101561138e5761137e83828151811061134e5761134d614aff565b5b60200260200101516000015184838151811061136d5761136c614aff565b5b602002602001015160200151613084565b61138781612dde565b9050611330565b50505060018081905550565b6113a26126b2565b73ffffffffffffffffffffffffffffffffffffffff166113c0611827565b73ffffffffffffffffffffffffffffffffffffffff1614611416576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161140d90614adf565b60405180910390fd5b61142060006138bb565b565b601260009054906101000a900460ff161561143c57600080fd5b60006114466126b2565b905060008173ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161148391906140c8565b602060405180830381865afa1580156114a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c491906147d2565b905060008273ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b815260040161150191906140c8565b602060405180830381865afa15801561151e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154291906147d2565b90507faa3e88aca472e90221daf7d3d601abafb62b120319089d7a2c2f63588da855298b8b8b8b8b8b8b8b8b8b8b6040516115879b9a9998979695949392919061483d565b60405180910390a15050505050505050505050565b600d6020528160005260406000206020528060005260406000206000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6115e73361275f565b600082829050905060005b818110156117105783838281811061160d5761160c614aff565b5b9050604002016000016020810190611625919061439d565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd333087878681811061165557611654614aff565b5b905060400201602001356040518463ffffffff1660e01b815260040161167d93929190614c82565b600060405180830381600087803b15801561169757600080fd5b505af11580156116ab573d6000803e3d6000fd5b505050506117008484838181106116c5576116c4614aff565b5b90506040020160000160208101906116dd919061439d565b8585848181106116f0576116ef614aff565b5b90506040020160200135336129ff565b61170981612dde565b90506115f2565b50505050565b6002600154141561175c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611753906149a5565b60405180910390fd5b60026001819055506000828290509050600081116117af576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a690614d2b565b60405180910390fd5b60005b8181101561181a5761180a8484838181106117d0576117cf614aff565b5b90506040020160000160208101906117e8919061439d565b8585848181106117fb576117fa614aff565b5b90506040020160200135613084565b61181381612dde565b90506117b2565b5050600180819055505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6118586126b2565b73ffffffffffffffffffffffffffffffffffffffff16611876611827565b73ffffffffffffffffffffffffffffffffffffffff16146118cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118c390614adf565b60405180910390fd5b80600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b6020528060005260406000206000915090508060000154908060010154908060020154908060030154905084565b60026001541415611986576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197d906149a5565b60405180910390fd5b60026001819055506000828290509050600081116119d9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119d090614c62565b60405180910390fd5b6119e233612cd4565b60005b81811015611a4d57611a3d848483818110611a0357611a02614aff565b5b9050604002016000016020810190611a1b919061439d565b858584818110611a2e57611a2d614aff565b5b90506040020160200135613084565b611a4681612dde565b90506119e5565b5050600180819055505050565b600c6020528160005260406000208181548110611a7657600080fd5b9060005260206000209060040201600091509150508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010154908060020154908060030154905084565b60036020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60075481565b7f000000000000000000000000917f7a44f93d76d70e254c96a2c7077375b369f181565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480611bb25750611b83611827565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b611bf1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be890614dbd565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611cbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cb590614939565b60405180910390fd5b60008251905060005b81811015611d4e5782600660008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000868481518110611d2457611d23614aff565b5b6020026020010151815260200190815260200160002081905550611d4781612dde565b9050611cc7565b5050505050565b611d5d6126b2565b73ffffffffffffffffffffffffffffffffffffffff16611d7b611827565b73ffffffffffffffffffffffffffffffffffffffff1614611dd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611dc890614adf565b60405180910390fd5b8060118190555050565b60085481565b611de96126b2565b73ffffffffffffffffffffffffffffffffffffffff16611e07611827565b73ffffffffffffffffffffffffffffffffffffffff1614611e5d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e5490614adf565b60405180910390fd5b81600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506004829080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505050565b60115481565b611f946126b2565b73ffffffffffffffffffffffffffffffffffffffff16611fb2611827565b73ffffffffffffffffffffffffffffffffffffffff1614612008576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fff90614adf565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612078576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161206f90614e4f565b60405180910390fd5b612081816138bb565b50565b600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061211257506120e3611827565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b612151576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161214890614dbd565b60405180910390fd5b60008383905090506000811161219c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161219390614ebb565b60405180910390fd5b600082116121df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121d690614f4d565b60405180910390fd5b60006001601060008282546121f49190614bc0565b9250508190555060005b828110156123f6576000600d600088888581811061221f5761221e614aff565b5b9050604002016000016020810190612237919061439d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600088888581811061228657612285614aff565b5b90506040020160200135815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146123e5576064600e6000601054815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546123589190614bc0565b92505081905550600f60006010548152602001908152602001600020819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001836123e29190614bc0565b92505b506123ef81612dde565b90506121fe565b506000811161243a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161243190614fdf565b60405180910390fd5b6000600f6000601054815260200190815260200160002080549050905060005b818110156126a9576000600f60006010548152602001908152602001600020828154811061248b5761248a614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506000600e6000601054815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115612698576000606487612522919061502e565b85600e6000601054815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612580919061502e565b61258a919061505f565b905080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282546125de9190614bc0565b9250508190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008282546126379190614bc0565b925050819055506000600e6000601054815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b506126a281612dde565b905061245a565b50505050505050565b600033905090565b6000600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546127579190614bc0565b905092915050565b6000600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905014156129fc576000600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002015411156128885742600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018190555042600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030181905550612998565b6040518060800160405280600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001548152602001600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154815260200142815260200142815250600b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301559050505b600a819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b8273ffffffffffffffffffffffffffffffffffffffff16600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612acc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ac390614939565b60405180910390fd5b600060405180608001604052808573ffffffffffffffffffffffffffffffffffffffff16815260200184815260200142815260200142815250905081600d60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506020820151816001015560408201518160020155606082015181600301555050600160086000828254612c799190614bc0565b925050819055508173ffffffffffffffffffffffffffffffffffffffff167fa5c6423d7b62a11b9a8a75349fbfc9865029364ea11b6ba86dabdaf8a406529d82604051612cc6919061512c565b60405180910390a250505050565b6000612cdf8261397f565b905042600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000828254612d7a9190614bc0565b9250508190555080600b60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000828254612dd39190614bc0565b925050819055505050565b6000600182019050919050565b600080600c60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015612ef357838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600182015481526020016002820154815260200160038201548152505081526020019060010190612e4d565b5050505090506000808251905060005b81811015613077576000612f77858381518110612f2357612f22614aff565b5b602002602001015160400151600b60008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030154613dde565b90506000612fc1868481518110612f9157612f90614aff565b5b602002602001015160000151878581518110612fb057612faf614aff565b5b6020026020010151602001516126ba565b9050871561304b578082612fd5919061505f565b60648260075460648a8881518110612ff057612fef614aff565b5b602002602001015160400151426130079190614b5d565b613011919061505f565b61301b919061502e565b613025919061505f565b61302f919061502e565b6130399190614b5d565b856130449190614bc0565b9450613065565b8181613057919061505f565b856130629190614bc0565b94505b505061307081612dde565b9050612f03565b5081935050505092915050565b3373ffffffffffffffffffffffffffffffffffffffff16600d60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613162576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161315990615193565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166323b872dd3033846040518463ffffffff1660e01b815260040161319f93929190614c82565b600060405180830381600087803b1580156131b957600080fd5b505af11580156131cd573d6000803e3d6000fd5b5050505060005b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490508110156136395781600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106132705761326f614aff565b5b90600052602060002090600402016001015414801561333c57508273ffffffffffffffffffffffffffffffffffffffff16600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106132f2576132f1614aff565b5b906000526020600020906004020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15613629573373ffffffffffffffffffffffffffffffffffffffff167fe4303adcf299b488ab61917b621f0c76d6e78519f605a66909dcfe7f0167753b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002083815481106133ca576133c9614aff565b5b90600052602060002090600402016040516133e591906152a1565b60405180910390a2600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061347b9190614b5d565b8154811061348c5761348b614aff565b5b9060005260206000209060040201600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002082815481106134eb576134ea614aff565b5b90600052602060002090600402016000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600182015481600101556002820154816002015560038201548160030155905050600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806135d0576135cf614b91565b5b6001900381819060005260206000209060040201600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201600090556003820160009055505090555b61363281612dde565b90506131d4565b506000600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080549050141561380d5760005b600a8054905081101561380b573373ffffffffffffffffffffffffffffffffffffffff16600a82815481106136c0576136bf614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156137fb57600a6001600a8054905061371b9190614b5d565b8154811061372c5761372b614aff565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600a828154811061376b5761376a614aff565b5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600a8054806137c5576137c4614b91565b5b6001900381819060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905590555b61380481612dde565b9050613688565b505b6000600d60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600860008282546138b09190614b5d565b925050819055505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015613a8757838290600052602060002090600402016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820154815260200160028201548152602001600382015481525050815260200190600101906139e1565b5050505090506000808251905060005b81811015613dd2576000613b0b858381518110613ab757613ab6614aff565b5b602002602001015160400151600b60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030154613dde565b90506000811115613dc157613b5c858381518110613b2c57613b2b614aff565b5b602002602001015160000151868481518110613b4b57613b4a614aff565b5b6020026020010151602001516126ba565b81613b67919061505f565b84613b729190614bc0565b9350601154811415613cde57600754600754600c60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208481548110613bd557613bd4614aff565b5b90600052602060002090600402016003015442613bf29190614b5d565b613bfc919061502e565b613c06919061505f565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613c5757613c56614aff565b5b906000526020600020906004020160030154613c739190614bc0565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613cc457613cc3614aff565b5b906000526020600020906004020160020181905550613dc0565b60075481613cec919061505f565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613d3d57613d3c614aff565b5b906000526020600020906004020160030154613d599190614bc0565b600c60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208381548110613daa57613da9614aff565b5b9060005260206000209060040201600201819055505b5b50613dcb81612dde565b9050613a97565b50819350505050919050565b600080429050600754601154613df4919061505f565b83613dff9190614bc0565b811115613e2457600754601154613e16919061505f565b83613e219190614bc0565b90505b6007548482613e339190614b5d565b613e3d919061502e565b91505092915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613e8582613e5a565b9050919050565b613e9581613e7a565b8114613ea057600080fd5b50565b600081359050613eb281613e8c565b92915050565b6000819050919050565b613ecb81613eb8565b8114613ed657600080fd5b50565b600081359050613ee881613ec2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112613f1357613f12613eee565b5b8235905067ffffffffffffffff811115613f3057613f2f613ef3565b5b602083019150836001820283011115613f4c57613f4b613ef8565b5b9250929050565b60008060008060008060008060c0898b031215613f7357613f72613e50565b5b6000613f818b828c01613ea3565b9850506020613f928b828c01613ea3565b9750506040613fa38b828c01613ea3565b9650506060613fb48b828c01613ed9565b955050608089013567ffffffffffffffff811115613fd557613fd4613e55565b5b613fe18b828c01613efd565b945094505060a089013567ffffffffffffffff81111561400457614003613e55565b5b6140108b828c01613efd565b92509250509295985092959890939650565b6000806040838503121561403957614038613e50565b5b600061404785828601613ea3565b925050602061405885828601613ed9565b9150509250929050565b61406b81613eb8565b82525050565b60006020820190506140866000830184614062565b92915050565b6000602082840312156140a2576140a1613e50565b5b60006140b084828501613ed9565b91505092915050565b6140c281613e7a565b82525050565b60006020820190506140dd60008301846140b9565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614131826140e8565b810181811067ffffffffffffffff821117156141505761414f6140f9565b5b80604052505050565b6000614163613e46565b905061416f8282614128565b919050565b600067ffffffffffffffff82111561418f5761418e6140f9565b5b614198826140e8565b9050602081019050919050565b82818337600083830152505050565b60006141c76141c284614174565b614159565b9050828152602081018484840111156141e3576141e26140e3565b5b6141ee8482856141a5565b509392505050565b600082601f83011261420b5761420a613eee565b5b813561421b8482602086016141b4565b91505092915050565b6000806000806080858703121561423e5761423d613e50565b5b600061424c87828801613ea3565b945050602061425d87828801613ea3565b935050604061426e87828801613ed9565b925050606085013567ffffffffffffffff81111561428f5761428e613e55565b5b61429b878288016141f6565b91505092959194509250565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6142dc816142a7565b82525050565b60006020820190506142f760008301846142d3565b92915050565b6000819050919050565b614310816142fd565b811461431b57600080fd5b50565b60008135905061432d81614307565b92915050565b6000806040838503121561434a57614349613e50565b5b60006143588582860161431e565b925050602061436985828601613ea3565b9150509250929050565b61437c816142fd565b82525050565b60006020820190506143976000830184614373565b92915050565b6000602082840312156143b3576143b2613e50565b5b60006143c184828501613ea3565b91505092915050565b60006080820190506143df6000830187614062565b6143ec6020830186614062565b6143f96040830185614062565b6144066060830184614062565b95945050505050565b60008083601f84011261442557614424613eee565b5b8235905067ffffffffffffffff81111561444257614441613ef3565b5b60208301915083604082028301111561445e5761445d613ef8565b5b9250929050565b6000806020838503121561447c5761447b613e50565b5b600083013567ffffffffffffffff81111561449a57614499613e55565b5b6144a68582860161440f565b92509250509250929050565b60006080820190506144c760008301876140b9565b6144d46020830186614062565b6144e16040830185614062565b6144ee6060830184614062565b95945050505050565b6000819050919050565b600061451c61451761451284613e5a565b6144f7565b613e5a565b9050919050565b600061452e82614501565b9050919050565b600061454082614523565b9050919050565b61455081614535565b82525050565b600060208201905061456b6000830184614547565b92915050565b600061457c82614523565b9050919050565b61458c81614571565b82525050565b60006020820190506145a76000830184614583565b92915050565b60006145b882613e7a565b9050919050565b6145c8816145ad565b81146145d357600080fd5b50565b6000813590506145e5816145bf565b92915050565b600067ffffffffffffffff821115614606576146056140f9565b5b602082029050602081019050919050565b600061462a614625846145eb565b614159565b9050808382526020820190506020840283018581111561464d5761464c613ef8565b5b835b8181101561467657806146628882613ed9565b84526020840193505060208101905061464f565b5050509392505050565b600082601f83011261469557614694613eee565b5b81356146a5848260208601614617565b91505092915050565b6000806000606084860312156146c7576146c6613e50565b5b60006146d5868287016145d6565b935050602084013567ffffffffffffffff8111156146f6576146f5613e55565b5b61470286828701614680565b925050604061471386828701613ed9565b9150509250925092565b6000806040838503121561473457614733613e50565b5b6000614742858286016145d6565b925050602061475385828601613ed9565b9150509250929050565b60008060006040848603121561477657614775613e50565b5b600084013567ffffffffffffffff81111561479457614793613e55565b5b6147a08682870161440f565b935093505060206147b386828701613ed9565b9150509250925092565b6000815190506147cc81613ec2565b92915050565b6000602082840312156147e8576147e7613e50565b5b60006147f6848285016147bd565b91505092915050565b600082825260208201905092915050565b600061481c83856147ff565b93506148298385846141a5565b614832836140e8565b840190509392505050565b600061012082019050614853600083018e6140b9565b614860602083018d6140b9565b61486d604083018c6140b9565b61487a606083018b614062565b818103608083015261488d81898b614810565b905081810360a08301526148a2818789614810565b90506148b160c08301866140b9565b6148be60e0830185614062565b6148cc610100830184614062565b9c9b505050505050505050505050565b600082825260208201905092915050565b7f4163655374616b696e673a20556e6b6f776e20436f6e74726163740000000000600082015250565b6000614923601b836148dc565b915061492e826148ed565b602082019050919050565b6000602082019050818103600083015261495281614916565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b600061498f601f836148dc565b915061499a82614959565b602082019050919050565b600060208201905081810360008301526149be81614982565b9050919050565b7f4163655374616b696e673a204e6f7468696e6720746f20636c61696d00000000600082015250565b60006149fb601c836148dc565b9150614a06826149c5565b602082019050919050565b60006020820190508181036000830152614a2a816149ee565b9050919050565b50565b6000614a416000836147ff565b9150614a4c82614a31565b600082019050919050565b6000606082019050614a6c60008301856140b9565b614a796020830184614062565b8181036040830152614a8a81614a34565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000614ac96020836148dc565b9150614ad482614a93565b602082019050919050565b60006020820190508181036000830152614af881614abc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614b6882613eb8565b9150614b7383613eb8565b925082821015614b8657614b85614b2e565b5b828203905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000614bcb82613eb8565b9150614bd683613eb8565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115614c0b57614c0a614b2e565b5b828201905092915050565b7f4163655374616b696e673a204e6f20546f6b656e7320666f756e640000000000600082015250565b6000614c4c601b836148dc565b9150614c5782614c16565b602082019050919050565b60006020820190508181036000830152614c7b81614c3f565b9050919050565b6000606082019050614c9760008301866140b9565b614ca460208301856140b9565b614cb16040830184614062565b949350505050565b7f4163655374616b696e673a204e6f20546f6b656e7320696e2063616c6c64617460008201527f6100000000000000000000000000000000000000000000000000000000000000602082015250565b6000614d156021836148dc565b9150614d2082614cb9565b604082019050919050565b60006020820190508181036000830152614d4481614d08565b9050919050565b7f4163655374616b696e673a204f6e6c79205245574152445f555044415445206f60008201527f72204f574e45522e000000000000000000000000000000000000000000000000602082015250565b6000614da76028836148dc565b9150614db282614d4b565b604082019050919050565b60006020820190508181036000830152614dd681614d9a565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000614e396026836148dc565b9150614e4482614ddd565b604082019050919050565b60006020820190508181036000830152614e6881614e2c565b9050919050565b7f4163655374616b696e673a204e6f20546f6b656e730000000000000000000000600082015250565b6000614ea56015836148dc565b9150614eb082614e6f565b602082019050919050565b60006020820190508181036000830152614ed481614e98565b9050919050565b7f4163655374616b696e673a204e6f20426f6e757320746f20626520646973747260008201527f6962757465640000000000000000000000000000000000000000000000000000602082015250565b6000614f376026836148dc565b9150614f4282614edb565b604082019050919050565b60006020820190508181036000830152614f6681614f2a565b9050919050565b7f4163655374616b696e673a204e6f2073686172657320746f206469737472696260008201527f7574650000000000000000000000000000000000000000000000000000000000602082015250565b6000614fc96023836148dc565b9150614fd482614f6d565b604082019050919050565b60006020820190508181036000830152614ff881614fbc565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061503982613eb8565b915061504483613eb8565b92508261505457615053614fff565b5b828204905092915050565b600061506a82613eb8565b915061507583613eb8565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156150ae576150ad614b2e565b5b828202905092915050565b6150c281613e7a565b82525050565b6150d181613eb8565b82525050565b6080820160008201516150ed60008501826150b9565b50602082015161510060208501826150c8565b50604082015161511360408501826150c8565b50606082015161512660608501826150c8565b50505050565b600060808201905061514160008301846150d7565b92915050565b7f4163655374616b696e673a204e6f7420796f757220746f6b656e2e2e2e000000600082015250565b600061517d601d836148dc565b915061518882615147565b602082019050919050565b600060208201905081810360008301526151ac81615170565b9050919050565b60008160001c9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006151f36151ee836151b3565b6151c0565b9050919050565b6000819050919050565b6000615217615212836151b3565b6151fa565b9050919050565b608082016000808301549050615233816151e0565b61524060008601826150b9565b506001830154905061525181615204565b61525e60208601826150c8565b506002830154905061526f81615204565b61527c60408601826150c8565b506003830154905061528d81615204565b61529a60608601826150c8565b5050505050565b60006080820190506152b6600083018461521e565b9291505056fea264697066735822122014cddb4bbe1d87685a22bd4ea40cc27813315a23ff054040b5b74e94b147c5a864736f6c634300080a0033

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

000000000000000000000000917f7a44f93d76d70e254c96a2c7077375b369f10000000000000000000000000000000000000000000000000000000000278d00

-----Decoded View---------------
Arg [0] : _rewardsToken (address): 0x917F7A44f93d76d70E254C96a2C7077375b369F1
Arg [1] : _rewardPeriodInSeconds (uint256): 2592000

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000917f7a44f93d76d70e254c96a2c7077375b369f1
Arg [1] : 0000000000000000000000000000000000000000000000000000000000278d00


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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