Contract Address Details

0x7A05280940A23749106D8Fb2cA4b10B9D1C89067

Contract Name
VotingEscrow
Creator
0xa333e1–0837ec at 0x20e815–c2865e
Balance
0 VLX
Tokens
Fetching tokens...
Transactions
33 Transactions
Transfers
28 Transfers
Gas Used
8,480,084
Last Balance Update
3235040
Contract name:
VotingEscrow




Optimization enabled
true
Compiler version
v0.8.4+commit.c7e474f2




Optimization runs
200
EVM Version
default




Verified at
2021-11-11T14:19:57.752552Z

Constructor Arguments

000000000000000000000000428d561f82bbb9322e5a634490722f26714d4dca0000000000000000000000007593e8fc59cb7fb3839a2b4815576c68b3df23ff000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012566f74652d657363726f77656420414d5054000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067665414d50540000000000000000000000000000000000000000000000000000

Arg [0] (address) : 0x428d561f82bbb9322e5a634490722f26714d4dca
Arg [1] (address) : 0x7593e8fc59cb7fb3839a2b4815576c68b3df23ff
Arg [2] (string) : Vote-escrowed AMPT
Arg [3] (string) : veAMPT

              

Contract source code

// SPDX-License-Identifier: MIT
// File: contracts/utils/SmartWalletWhitelist.sol


pragma solidity ^0.8.0;

/**
 * @dev Interface for checking whether address belongs to a whitelisted type of a smart wallet.
 * When new types are added - the whole contract is changed
 * The check() method is modifying to be able to use caching
 * for individual wallet addresses
*/
interface SmartWalletChecker {
    function check(address) external view returns (bool);
}

contract SmartWalletWhitelist {
    address public admin;
    address public checker;

    mapping(address => bool) public wallets;
    
    event ApproveWallet(address);
    event RevokeWallet(address);

    event CheckerChanged(address oldChecker, address newChecker);
    
    constructor() {
        admin = msg.sender;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }
    
    function setChecker(address _checker) external onlyAdmin {
        address currentChecker = checker;
        require(_checker == address(0), "Can't set zero address");
        emit CheckerChanged(currentChecker, checker);
        checker = _checker;
    }
    
    function approveWallet(address _wallet) external onlyAdmin {
        require(_wallet != address(0), "Can't approve zero address");
        wallets[_wallet] = true;
        emit ApproveWallet(_wallet);
    }

    function revokeWallet(address _wallet) external onlyAdmin {
        require(_wallet != address(0), "Can't revoke zero address");
        wallets[_wallet] = false;
        emit RevokeWallet(_wallet);
    }
    
    function check(address _wallet) external view returns (bool) {
        if (wallets[_wallet]) {
            return true;
        } else if (checker != address(0)) {
            return SmartWalletChecker(checker).check(_wallet);
        }
        return false;
    }
}
// File: contracts/Governance/IAMPT.sol


pragma solidity ^0.8.0;

interface IAMPT {
    function balanceOf(address account) external view returns (uint256);
    function totalSupply() external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/Governance/VotingEscrow.sol


pragma solidity 0.8.4;
/*
# Voting escrow to have time-weighted votes
# Votes have a weight depending on time, so that users are committed
# to the future of (whatever they are voting for).
# The weight in this implementation is linear, and lock cannot be more than maxtime:
# w ^
# 1 +        /
#   |      /
#   |    /
#   |  /
#   |/
# 0 +--------+------> time
#       maxtime (4 years?)
*/



contract VotingEscrow {
    struct Point {
        int256 bias;
        int256 slope;
        uint256 ts;
        uint256 block;
    }

    struct Balance {
        uint256 amount;
        uint256 end;
    }

    enum Action {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME
    }

    /// @notice EIP-20 token name for this token
    string private _name;

    /// @notice EIP-20 token symbol for this token
    string private _symbol;

    bool private _entered;

    address public admin;
    IAMPT public amptToken;
    SmartWalletChecker public smartWalletChecker;

    mapping(address => Balance) private _lockedBalances;
    mapping(address => Balance) private _operationBalances;
    uint256 private _totalSupply;

    uint256 public epoch;
    mapping(uint256 => Point) public pointHistory;

    /// @dev A record of each account's delegate
    mapping (address => address) public delegates;

    /// @notice A record of states for signing / validating signatures
    mapping (address => uint256) public nonces;

    /// @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

    /// @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

    uint256 internal constant WEEK = 604800; // 7 * 24 * 3600;
    uint256 internal constant MAXCAP = 126144000; // 4 * 365 * 24 * 3600;
    
    mapping(address => mapping(uint256 => Point)) public userPointHistory;
    mapping(address => uint256) public userPointEpoch;
    mapping(uint256 => int256) public slopeChanges;

    event Deposited(address indexed provider, uint256 value, uint256 indexed lockTime, uint256 actionType);
    event Withdrawn(address indexed provider, uint256 value);

    /// @notice An event that's emitted when an account changes their delegate
    event DelegateChanged(address indexed delegator, address indexed toDelegate);

    /// @notice An event that's emitted when an smart wallet Checker is changed
    event SmartWalletCheckedChanged(address oldChecked, address newChecker);

     /**
     * @notice Contract constructor
     * @param amptToken_ `AMPT` token address
     * @param smartWalletChecker_ SmartWalletChecker contract address
     * @param name_ Token name
     * @param symbol_ Token symbol
    */
    constructor(IAMPT amptToken_, SmartWalletChecker smartWalletChecker_, string memory name_, string memory symbol_) {
        amptToken = amptToken_;
        _name = name_;
        _symbol = symbol_;
        
        admin = msg.sender;

        smartWalletChecker = smartWalletChecker_;

        pointHistory[0].block = getBlockNumber();
        pointHistory[0].ts = getBlockTimestamp();

        _entered = false;
    }

    modifier onlyAllowed(address addr) {
        if (addr != tx.origin) {
            require(smartWalletChecker.check(addr), "Smart contract depositors not allowed");
        }
        _;
    }

    modifier nonReentrant() {
        require(!_entered, "reentrant call");
        _entered = true;
        _;
        _entered = false;
    }

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

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

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() external pure returns (uint8) {
        return 18;
    }
    
    /**
     * @dev Returns the amount of locked tokens in existence.
    */
    function totalSupply() external view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @notice Calculate total voting power
     * @return Total voting power
    */
    function votePower() external view returns (uint256) {
        return _supplyAt(getBlockTimestamp());
    }

    /**
     * @notice Calculate total voting power at some point in the past
     * @param block_ Block to calculate the total voting power at
     * @return Total voting power ar `block`
    */
    function votePowerAt(uint256 block_) external view returns (uint256) {
        uint256 currentTimestamp = getBlockTimestamp();
        uint256 currentBlock = getBlockNumber();

        require(currentBlock >= block_, "Block must be in the past");
        
        uint256 _targetEpoch = findBlockEpoch(block_, epoch);
        Point memory point = pointHistory[_targetEpoch];
        uint256 dt = 0;

        if (epoch > _targetEpoch) {
            Point memory nextPoint = pointHistory[_targetEpoch + 1];
            if (point.block != nextPoint.block) {
                dt = (block_ - point.block) * (nextPoint.ts - point.ts) / (nextPoint.block - point.block);
            }
        } else if (point.block != currentBlock) {
            dt = (block_ - point.block) * (currentTimestamp - point.ts) / (currentBlock - point.block);
        }

        return _supplyAt(point.ts + dt);
    }

    /**
     * @notice Calculate total voting power at some point in the past
     * @param timestamp Time to calculate the total voting power at
     * @return Total voting power at that time
    */
    function _supplyAt(uint256 timestamp) internal view returns (uint256) {
        Point memory point = pointHistory[epoch];
        uint256 timeIndex = point.ts * WEEK / WEEK;

        for(int i=0; i <= 255; i++) {
            timeIndex += WEEK;
            int256 dSlope = 0;

            if (timeIndex > timestamp) {
                timeIndex = timestamp;
            } else {
                dSlope = slopeChanges[timeIndex];
            }

            point.bias -= point.slope * int256(timeIndex - point.ts);
            if (timeIndex == timestamp) {
                break;
            }
            point.slope += dSlope;
            point.ts = timeIndex;
        }

        if (point.bias < 0) {
            point.bias = 0;
        }
        return uint256(point.bias);
    }

        /**
     * @notice Binary search to estimate timestamp for block number
     * @param block_ Block to find
     * @param epoch_ Don't go beyond this epoch
     * @return Approximate timestamp for block
    */
    function findBlockEpoch(uint256 block_, uint256 epoch_) internal view returns (uint256)  {
        uint256 _min = 0;
        uint256 _max = epoch_;
        for(int i=0; i <= 128; i++) {
            if (_min >= _max) break;

            uint256 _mid = (_min + _max + 1) / 2;

            if (pointHistory[_mid].block <= block_) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }
        return _min;
    }

    /**
     * @notice Record global data to checkpoint
    */
    function checkpoint() external {
        _checkpoint(address(0), Balance(0,0), Balance(0,0));
    }

    /**
     * @notice Get the current voting power for `msg.sender`
     * @param addr User wallet address
     * @return User voting power
    */
    function balanceOf(address addr) external view returns (uint256) {
        uint256 _epoch = userPointEpoch[addr];
        uint256 currentTimestamp = getBlockTimestamp();

        if (_epoch == 0) {
            return 0;
        } else {
            Point memory lastPoint = userPointHistory[addr][_epoch];
            lastPoint.bias -= lastPoint.slope * int256(currentTimestamp - lastPoint.ts);
            if (lastPoint.bias < 0) {
                lastPoint.bias = 0;
            }
            return uint256(lastPoint.bias);
        }
    }

    /**
     * @dev Returns the amount of tokens owner by `addr`.
    */
    function locked(address addr) external view returns (Balance memory) {
        return _lockedBalances[addr];
    }

    /**
     * @dev Returns the amount of tokens owner by `addr` used for calculating vote power.
    */
    function operationLocked(address addr) external view returns (Balance memory) {
        return _operationBalances[addr];
    }

    function changeSmartWalletChecker(SmartWalletChecker newSmartWalletChecker) external {
        SmartWalletChecker currentWalletChecker = smartWalletChecker;
        require(msg.sender == admin, "Only admin can change smart wallet checker");
        require(newSmartWalletChecker != currentWalletChecker, "New smart wallet checker is the same as the old one");
        smartWalletChecker = newSmartWalletChecker;
        emit SmartWalletCheckedChanged(address(currentWalletChecker), address(newSmartWalletChecker));
    }

    /**
     * @notice Deposit `value` tokens for `msg.sender` and lock until `unlockTime`
     * @param value Amount to deposit
     * @param unlockTime Epoch time when tokens unlock, rounded down to whole weeks
    */
    function createLock(uint256 value, uint256 unlockTime) external nonReentrant onlyAllowed(msg.sender)  {
        require(value > 0, "Value must be greater than 0");

        Balance storage balance = _lockedBalances[msg.sender];
        require(balance.amount == 0, "Withdraw old tokens first");

        uint currentTimestamp = getBlockTimestamp();
        require(unlockTime > currentTimestamp, "Unlock time must be in the future");
        require(currentTimestamp + MAXCAP >= unlockTime, "Voting lock can be 4 years max");

        _depositFor(msg.sender, value, unlockTime, uint256(Action.CREATE_LOCK_TYPE));
    }

    /**
     * @notice Deposit `value` additional tokens for `msg.sender` without modifying the unlock time
     * @param value Amount of tokens to deposit and add to the lock
    */
    function increaseLockAmount(uint256 value) external nonReentrant onlyAllowed(msg.sender) {
        require(value > 0, "Value must be greater than 0");
        Balance storage balance = _lockedBalances[msg.sender]; 

        require(balance.amount > 0, "No existing lock found");
        require(balance.end > getBlockTimestamp(), "Cannot add to expired lock. Withdraw");

        _depositFor(msg.sender, value, 0, uint256(Action.INCREASE_LOCK_AMOUNT));
    }

    /**
     * @notice Extend the unlock time for `msg.sender` to `unlockTime`
     * @param newLockTime New epoch time for unlocking
    */
    function increaseLockTime(uint256 newLockTime) external nonReentrant onlyAllowed(msg.sender) {
        uint256 currentTimestamp = getBlockTimestamp();
        require(currentTimestamp + MAXCAP >= newLockTime, "Voting lock can be 4 years max");

        Balance storage balance = _lockedBalances[msg.sender]; 
        require(balance.amount > 0, "Nothing is locked");
        require(newLockTime > balance.end, "Lock time must be greater");
        require(balance.end > currentTimestamp, "Lock expired");

        _depositFor(msg.sender, 0, newLockTime, uint256(Action.INCREASE_UNLOCK_TIME));
    }

    /**
     * @notice Withdraw all tokens for `msg.sender`
     * @dev Only possible if the lock has expired
    */
    function withdraw() external nonReentrant {
        Balance storage _locked = _lockedBalances[msg.sender];
        Balance storage _operationLocked = _operationBalances[msg.sender];

        require(getBlockTimestamp() >= _locked.end, "Cannot withdraw before lock expires");
        uint256 value = _locked.amount;

        Balance memory _oldOperationLocked = _operationLocked;
        _totalSupply -= value;

        _locked.amount = 0;
        _operationLocked.amount = 0;

        _locked.end = 0;
        _operationLocked.end = 0;

        _checkpoint(msg.sender, _oldOperationLocked, _operationLocked);

        emit Withdrawn(msg.sender, value);
        assert(amptToken.transfer(msg.sender, value));
    }

    /**
     * @notice Deposit `value` tokens for `depositer` and add to the lock
     * @dev Anyone (even a smart contract) can deposit for someone else, but cannot extend their locktime and deposit for a brand new use
     * @param depositer User's wallet address
     * @param value Amount to add to user's lock
    */
    function depositFor(address depositer, uint256 value) external nonReentrant {
        Balance storage _locked = _lockedBalances[depositer]; 

        require(value > 0, "Value must be greater than 0");
        require(_locked.amount > 0, "No existing lock found");
        require(_locked.end > getBlockTimestamp(), "Cannot add to expired lock. Withdraw");

        _depositFor(depositer, value, 0, uint256(Action.DEPOSIT_FOR_TYPE));
    }

    /**
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegatee The address to delegate votes to
     */
    function delegate(address delegatee) external onlyAllowed(msg.sender) {
        require(msg.sender != delegatee, "Cannot delegate to self");

        require(msg.sender != address(0), "Cannot delegate from the zero address");
        require(delegatee != address(0), "Cannot delegate to the zero address");
        
        _delegate(msg.sender, delegatee);
    }

    /**
     * @notice Delegates votes from signatory to `delegatee`
     * @param delegatee The address to delegate votes to
     * @param nonce The contract state required to match the signature
     * @param expiry The time at which to expire the signature
     * @param v The recovery byte of the signature
     * @param r Half of the ECDSA signature pair
     * @param s Half of the ECDSA signature pair
     */
    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external {
        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(_name)), getChainId(), address(this)));
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "delegateBySig: invalid signature");
        require(signatory != msg.sender, "delegateBySig: cannot delegate to self");

        require(nonce == nonces[signatory], "delegateBySig: invalid nonce");
        nonces[signatory]++;
        require(getBlockTimestamp() <= expiry, "delegateBySig: signature expired");

        _delegate(signatory, delegatee);
    }

    /**
     * @notice Deposit and lock tokens for a user
     * @param depositer User's wallet address
     * @param value Amount to add to user's lock
     * @param unlockTime New time when to unlock the tokens, or 0 if unchanged
     * @param actionType Type of action to log
    */
    function _depositFor(address depositer, uint256 value, uint256 unlockTime, uint256 actionType) internal {
        Balance storage _locked = _lockedBalances[depositer];
        Balance storage _operationLocked = _operationBalances[depositer];
        Balance memory _oldOperationLocked = _operationLocked;

        _totalSupply += value;

        _locked.amount += value;
        if (unlockTime != 0) {
            _locked.end = unlockTime;
        }

        _operationLocked.amount += value;
        if (unlockTime != 0) {
            _operationLocked.end = unlockTime;
        }

        _checkpoint(depositer, _oldOperationLocked, _operationLocked);

        emit Deposited(depositer, value, _locked.end, actionType);
        if(value != 0) {
            assert(amptToken.transferFrom(depositer, address(this), value));
        }
    }

    function _delegate(address delegator, address delegatee) internal {
        Balance storage _sourceBalance = _operationBalances[delegator];
        Balance memory _oldSourceBalance = _sourceBalance;

        require(_sourceBalance.amount > 0, "No existing lock found");

        Balance storage _destinationBalance = _operationBalances[delegatee];
        Balance memory _oldDestinationBalance = _destinationBalance;

        delegates[delegator] = delegatee;
        emit DelegateChanged(delegator, delegatee);

        _sourceBalance.amount = 0;
        /// @dev The balance.end should be intact for the future deposits
        _checkpoint(delegator, _oldSourceBalance, _sourceBalance);

        _destinationBalance.amount += _oldSourceBalance.amount;
        if(_oldDestinationBalance.end == 0) {
            _destinationBalance.end = _oldSourceBalance.end;
        }
        _checkpoint(delegatee, _oldDestinationBalance, _destinationBalance);
    }

    struct CheckPointVars {
        int256 oldDslope;
        int256 newDslope;
        uint256 currentEpoch;
        uint256 currentBlock;
        uint256 currentTimestamp;
        uint256 currentUserEpoch;
    }

    /**
     * @notice Record global and per-user data to checkpoint
     * @param addr User's wallet address. No user checkpoint if 0x0
     * @param oldLocked Previous locked amount / end lock time for the user
     * @param newLocked New locked amount / end lock time for the user
    */
    function _checkpoint(address addr, Balance memory oldLocked, Balance memory newLocked) internal {
        Point memory _userPointOld = Point(0, 0, 0, 0);
        Point memory _userPointNew = Point(0, 0, 0, 0);

        CheckPointVars memory _checkpointVars = CheckPointVars(
            0, 
            0, 
            epoch, 
            getBlockNumber(), 
            getBlockTimestamp(), 
            userPointEpoch[addr]
        );

        if (addr != address(0)) {
            if (oldLocked.end > _checkpointVars.currentTimestamp && oldLocked.amount > 0) {
                _userPointOld.slope = int256(oldLocked.amount / MAXCAP);
                _userPointOld.bias = _userPointOld.slope * int256(oldLocked.end - _checkpointVars.currentTimestamp);
            }

            if (newLocked.end > _checkpointVars.currentTimestamp && newLocked.amount > 0) {
                _userPointNew.slope = int256(newLocked.amount / MAXCAP);
                _userPointNew.bias = _userPointNew.slope * int256(newLocked.end - _checkpointVars.currentTimestamp);
            }

            _checkpointVars.oldDslope = slopeChanges[oldLocked.end];
            if (newLocked.end != 0) {
                if (newLocked.end == oldLocked.end) {
                    _checkpointVars.newDslope = _checkpointVars.oldDslope;
                } else {
                    _checkpointVars.newDslope = slopeChanges[newLocked.end];
                }
            }
        }

        Point memory lastPoint = Point(0, 0, _checkpointVars.currentTimestamp, _checkpointVars.currentBlock);
        if (_checkpointVars.currentEpoch > 0) {
            lastPoint = pointHistory[_checkpointVars.currentEpoch];
        }

        uint lastCheckpoint = lastPoint.ts;
        Point memory initialLastPoint = lastPoint;
        uint256 blockSlope = 0;
        if (_checkpointVars.currentTimestamp > lastPoint.ts) {
            blockSlope = 1e18 * (_checkpointVars.currentBlock - lastPoint.block) / (_checkpointVars.currentTimestamp - lastPoint.ts);
        }

        uint256 timeIndex = lastCheckpoint * WEEK / WEEK;
        for (int256 i=0; i <= 255; i++) {
            timeIndex += WEEK;
            int256 dSlope = 0;

            if (timeIndex > _checkpointVars.currentTimestamp) {
                timeIndex = _checkpointVars.currentTimestamp;
            } else {
                dSlope = slopeChanges[timeIndex];
            }

            lastPoint.bias -= lastPoint.slope * int256(timeIndex - lastCheckpoint);
            lastPoint.slope += dSlope;
            if (lastPoint.bias < 0) {
                lastPoint.bias = 0;
            }
            if (lastPoint.slope < 0) {
                lastPoint.slope = 0;
            }
            lastCheckpoint = timeIndex;
            lastPoint.ts = timeIndex;

            lastPoint.block = initialLastPoint.block + blockSlope * (timeIndex - initialLastPoint.ts) / 1e18;
            _checkpointVars.currentEpoch += 1;
            if (timeIndex == _checkpointVars.currentTimestamp) {
                lastPoint.block = _checkpointVars.currentBlock;
                break;
            } else {
                pointHistory[_checkpointVars.currentEpoch] = lastPoint;
            }
        }
        epoch = _checkpointVars.currentEpoch;

        if (addr != address(0)) {
            lastPoint.slope += (_userPointNew.slope - _userPointOld.slope);
            lastPoint.bias += (_userPointNew.bias - _userPointOld.bias);
            if (lastPoint.slope < 0) {
                lastPoint.slope = 0;
            }
            if (lastPoint.bias < 0) {
                lastPoint.bias = 0;
            }
        }
        pointHistory[_checkpointVars.currentEpoch] = lastPoint;

        if (addr != address(0)) {
            if (oldLocked.end > _checkpointVars.currentTimestamp) {
                _checkpointVars.oldDslope += _userPointOld.slope;
                if (newLocked.end == oldLocked.end) {
                    _checkpointVars.oldDslope -= _userPointNew.slope;
                }
                slopeChanges[oldLocked.end] = _checkpointVars.oldDslope;
            }
            if (newLocked.end > _checkpointVars.currentTimestamp) {
                if (newLocked.end > oldLocked.end) {
                    _checkpointVars.newDslope -= _userPointNew.slope;
                    slopeChanges[newLocked.end] = _checkpointVars.newDslope;
                }
            }

            userPointEpoch[addr]++;
            _userPointNew.ts = _checkpointVars.currentTimestamp;
            _userPointNew.block = _checkpointVars.currentBlock;
            userPointHistory[addr][_checkpointVars.currentUserEpoch + 1] = _userPointNew;
        }
    }

    function getBlockNumber() public virtual view returns (uint256) {
        return block.number;
    }

    function getBlockTimestamp() public virtual view returns (uint256) {
        return block.timestamp;
    }

    function getChainId() internal view returns (uint256) {
        uint256 chainId;
        assembly { chainId := chainid() }
        return chainId;
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"amptToken_","internalType":"contract IAMPT"},{"type":"address","name":"smartWalletChecker_","internalType":"contract SmartWalletChecker"},{"type":"string","name":"name_","internalType":"string"},{"type":"string","name":"symbol_","internalType":"string"}]},{"type":"event","name":"DelegateChanged","inputs":[{"type":"address","name":"delegator","internalType":"address","indexed":true},{"type":"address","name":"toDelegate","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Deposited","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"lockTime","internalType":"uint256","indexed":true},{"type":"uint256","name":"actionType","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SmartWalletCheckedChanged","inputs":[{"type":"address","name":"oldChecked","internalType":"address","indexed":false},{"type":"address","name":"newChecker","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Withdrawn","inputs":[{"type":"address","name":"provider","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DELEGATION_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_TYPEHASH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"admin","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAMPT"}],"name":"amptToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"changeSmartWalletChecker","inputs":[{"type":"address","name":"newSmartWalletChecker","internalType":"contract SmartWalletChecker"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"checkpoint","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createLock","inputs":[{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"unlockTime","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegate","inputs":[{"type":"address","name":"delegatee","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegateBySig","inputs":[{"type":"address","name":"delegatee","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"delegates","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"depositFor","inputs":[{"type":"address","name":"depositer","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"epoch","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBlockTimestamp","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"increaseLockAmount","inputs":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"increaseLockTime","inputs":[{"type":"uint256","name":"newLockTime","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct VotingEscrow.Balance","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"end","internalType":"uint256"}]}],"name":"locked","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct VotingEscrow.Balance","components":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"end","internalType":"uint256"}]}],"name":"operationLocked","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int256","name":"bias","internalType":"int256"},{"type":"int256","name":"slope","internalType":"int256"},{"type":"uint256","name":"ts","internalType":"uint256"},{"type":"uint256","name":"block","internalType":"uint256"}],"name":"pointHistory","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int256","name":"","internalType":"int256"}],"name":"slopeChanges","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract SmartWalletChecker"}],"name":"smartWalletChecker","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"userPointEpoch","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"int256","name":"bias","internalType":"int256"},{"type":"int256","name":"slope","internalType":"int256"},{"type":"uint256","name":"ts","internalType":"uint256"},{"type":"uint256","name":"block","internalType":"uint256"}],"name":"userPointHistory","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"votePower","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"votePowerAt","inputs":[{"type":"uint256","name":"block_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[]}]
            

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101f05760003560e01c8063796b89b91161010f578063b52c05fe116100a2578063e7a324dc11610071578063e7a324dc14610536578063f17c9d761461055d578063f52a36f714610565578063f851a4401461058557600080fd5b8063b52c05fe146104b3578063c2c4c5c1146104c6578063c3cda520146104ce578063cbf9fe5f146104e157600080fd5b806381fc83bb116100de57806381fc83bb1461044d5780638ad4c4471461046d578063900cf0cf146104a257806395d89b41146104ab57600080fd5b8063796b89b91461040157806379af55e4146104075780637df148541461041a5780637ecebe001461042d57600080fd5b80633e548fb111610187578063587cde1e11610156578063587cde1e146103875780635c19a95c146103c857806366e80391146103db57806370a08231146103ee57600080fd5b80633e548fb1146102eb578063403f44471461035b57806342cbb15c1461036e57806352ff9c691461037457600080fd5b80632f4f21e2116101c35780632f4f21e214610261578063313ce5671461027457806334d901a4146102835780633ccfd60b146102e357600080fd5b806306fdde03146101f55780630f7d84771461021357806318160ddd1461022857806320606b701461023a575b600080fd5b6101fd61059d565b60405161020a9190612485565b60405180910390f35b6102266102213660046122e3565b61062f565b005b6007545b60405190815260200161020a565b61022c7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61022661026f366004612306565b61078b565b6040516012815260200161020a565b6102c3610291366004612306565b600c60209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b60408051948552602085019390935291830152606082015260800161020a565b61022661084d565b6103406102f93660046122e3565b6040805180820190915260008082526020820152506001600160a01b0316600090815260066020908152604091829020825180840190935280548352600101549082015290565b6040805182518152602092830151928101929092520161020a565b6102266103693660046123b1565b610a50565b4361022c565b61022c6103823660046123b1565b610ba1565b6103b06103953660046122e3565b600a602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161020a565b6102266103d63660046122e3565b610d77565b6003546103b0906001600160a01b031681565b61022c6103fc3660046122e3565b610f3e565b4261022c565b6102266104153660046123b1565b610ffb565b6004546103b0906001600160a01b031681565b61022c61043b3660046122e3565b600b6020526000908152604090205481565b61022c61045b3660046122e3565b600d6020526000908152604090205481565b6102c361047b3660046123b1565b60096020526000908152604090208054600182015460028301546003909301549192909184565b61022c60085481565b6101fd611220565b6102266104c13660046123c9565b61122f565b610226611455565b6102266104dc366004612331565b61148f565b6103406104ef3660046122e3565b6040805180820190915260008082526020820152506001600160a01b0316600090815260056020908152604091829020825180840190935280548352600101549082015290565b61022c7fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b61022c6117c3565b61022c6105733660046123b1565b600e6020526000908152604090205481565b6002546103b09061010090046001600160a01b031681565b6060600080546105ac90612761565b80601f01602080910402602001604051908101604052809291908181526020018280546105d890612761565b80156106255780601f106105fa57610100808354040283529160200191610625565b820191906000526020600020905b81548152906001019060200180831161060857829003601f168201915b5050505050905090565b6004546002546001600160a01b03918216913361010090920416146106ae5760405162461bcd60e51b815260206004820152602a60248201527f4f6e6c792061646d696e2063616e206368616e676520736d6172742077616c6c60448201526932ba1031b432b1b5b2b960b11b60648201526084015b60405180910390fd5b806001600160a01b0316826001600160a01b0316141561072c5760405162461bcd60e51b815260206004820152603360248201527f4e657720736d6172742077616c6c657420636865636b6572206973207468652060448201527273616d6520617320746865206f6c64206f6e6560681b60648201526084016106a5565b600480546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f1b0671b188e3268797d7e11abc653043fa932164a023cb5b6098a26f930fc64b910160405180910390a15050565b60025460ff16156107ae5760405162461bcd60e51b81526004016106a590612598565b6002805460ff191660011790556001600160a01b0382166000908152600560205260409020816107f05760405162461bcd60e51b81526004016106a590612561565b805461080e5760405162461bcd60e51b81526004016106a5906125c0565b428160010154116108315760405162461bcd60e51b81526004016106a59061251d565b61083e83836000806117d3565b50506002805460ff1916905550565b60025460ff16156108705760405162461bcd60e51b81526004016106a590612598565b6002805460ff191660019081179091553360009081526005602090815260408083206006909252909120918101549091906108a84290565b10156109025760405162461bcd60e51b815260206004820152602360248201527f43616e6e6f74207769746864726177206265666f7265206c6f636b206578706960448201526272657360e81b60648201526084016106a5565b81546040805180820190915282548152600183015460208201526007805483919060009061093190849061274a565b909155505060008085558084556001808601829055840181905560408051808201909152818152602081019190915261096d9033908390611996565b60405182815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a260035460405163a9059cbb60e01b8152336004820152602481018490526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b1580156109ee57600080fd5b505af1158015610a02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a269190612391565b610a4057634e487b7160e01b600052600160045260246000fd5b50506002805460ff191690555050565b60025460ff1615610a735760405162461bcd60e51b81526004016106a590612598565b6002805460ff1916600117905533328114610b235760048054604051631846d2f560e31b81526001600160a01b038481169382019390935291169063c23697a89060240160206040518083038186803b158015610acf57600080fd5b505afa158015610ae3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b079190612391565b610b235760405162461bcd60e51b81526004016106a5906124d8565b60008211610b435760405162461bcd60e51b81526004016106a590612561565b3360009081526005602052604090208054610b705760405162461bcd60e51b81526004016106a5906125c0565b42816001015411610b935760405162461bcd60e51b81526004016106a59061251d565b61083e3384600060026117d3565b6000424383811015610bf55760405162461bcd60e51b815260206004820152601960248201527f426c6f636b206d75737420626520696e2074686520706173740000000000000060448201526064016106a5565b6000610c0385600854611feb565b600081815260096020908152604080832081516080810183528154815260018201549381019390935260028101549183019190915260030154606082015260085492935091831015610d05576000600981610c5f866001612631565b815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505090508060600151836060015114610cff5782606001518160600151610cc5919061274a565b83604001518260400151610cd9919061274a565b6060850151610ce8908b61274a565b610cf291906126ec565b610cfc9190612649565b91505b50610d54565b83826060015114610d54576060820151610d1f908561274a565b6040830151610d2e908761274a565b6060840151610d3d908a61274a565b610d4791906126ec565b610d519190612649565b90505b610d6c818360400151610d679190612631565b612076565b979650505050505050565b33328114610e1a5760048054604051631846d2f560e31b81526001600160a01b038481169382019390935291169063c23697a89060240160206040518083038186803b158015610dc657600080fd5b505afa158015610dda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dfe9190612391565b610e1a5760405162461bcd60e51b81526004016106a5906124d8565b336001600160a01b0383161415610e735760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f742064656c656761746520746f2073656c6600000000000000000060448201526064016106a5565b33610ece5760405162461bcd60e51b815260206004820152602560248201527f43616e6e6f742064656c65676174652066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016106a5565b6001600160a01b038216610f305760405162461bcd60e51b815260206004820152602360248201527f43616e6e6f742064656c656761746520746f20746865207a65726f206164647260448201526265737360e81b60648201526084016106a5565b610f3a3383612196565b5050565b6001600160a01b0381166000908152600d60205260408120544281610f67575060009392505050565b6001600160a01b0384166000908152600c60209081526040808320858452825291829020825160808101845281548152600182015492810192909252600281015492820183905260030154606082015290610fc2908361274a565b8160200151610fd19190612669565b81518290610fe090839061270b565b905250805160001315610ff257600081525b51949350505050565b60025460ff161561101e5760405162461bcd60e51b81526004016106a590612598565b6002805460ff19166001179055333281146110ce5760048054604051631846d2f560e31b81526001600160a01b038481169382019390935291169063c23697a89060240160206040518083038186803b15801561107a57600080fd5b505afa15801561108e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b29190612391565b6110ce5760405162461bcd60e51b81526004016106a5906124d8565b42826110de630784ce0083612631565b101561112c5760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016106a5565b336000908152600560205260409020805461117d5760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b60448201526064016106a5565b806001015484116111d05760405162461bcd60e51b815260206004820152601960248201527f4c6f636b2074696d65206d75737420626520677265617465720000000000000060448201526064016106a5565b818160010154116112125760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b60448201526064016106a5565b610a403360008660036117d3565b6060600180546105ac90612761565b60025460ff16156112525760405162461bcd60e51b81526004016106a590612598565b6002805460ff19166001179055333281146113025760048054604051631846d2f560e31b81526001600160a01b038481169382019390935291169063c23697a89060240160206040518083038186803b1580156112ae57600080fd5b505afa1580156112c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e69190612391565b6113025760405162461bcd60e51b81526004016106a5906124d8565b600083116113225760405162461bcd60e51b81526004016106a590612561565b3360009081526005602052604090208054156113805760405162461bcd60e51b815260206004820152601960248201527f5769746864726177206f6c6420746f6b656e732066697273740000000000000060448201526064016106a5565b428084116113da5760405162461bcd60e51b815260206004820152602160248201527f556e6c6f636b2074696d65206d75737420626520696e207468652066757475726044820152606560f81b60648201526084016106a5565b836113e9630784ce0083612631565b10156114375760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016106a5565b61144433868660016117d3565b50506002805460ff19169055505050565b61148d600060405180604001604052806000815260200160008152506040518060400160405280600081526020016000815250611996565b565b60007f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86660006040516114c191906123ea565b60405180910390206114d04690565b60408051602080820195909552808201939093526060830191909152306080808401919091528151808403909101815260a0830182528051908401207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60c08401526001600160a01b038b1660e084015261010083018a90526101208084018a90528251808503909101815261014084019092528151919093012061190160f01b610160830152610162820183905261018282018190529192506000906101a20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa1580156115fc573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661165f5760405162461bcd60e51b815260206004820181905260248201527f64656c656761746542795369673a20696e76616c6964207369676e617475726560448201526064016106a5565b6001600160a01b0381163314156116c75760405162461bcd60e51b815260206004820152602660248201527f64656c656761746542795369673a2063616e6e6f742064656c65676174652074604482015265379039b2b63360d11b60648201526084016106a5565b6001600160a01b0381166000908152600b6020526040902054891461172e5760405162461bcd60e51b815260206004820152601c60248201527f64656c656761746542795369673a20696e76616c6964206e6f6e63650000000060448201526064016106a5565b6001600160a01b0381166000908152600b60205260408120805491611752836127bc565b91905055508761175f4290565b11156117ad5760405162461bcd60e51b815260206004820181905260248201527f64656c656761746542795369673a207369676e6174757265206578706972656460448201526064016106a5565b6117b7818b612196565b50505050505050505050565b60006117ce42612076565b905090565b6001600160a01b0384166000908152600560209081526040808320600683528184208251808401909352805483526001810154938301939093526007805491948892611820908490612631565b9091555050825486908490600090611839908490612631565b9091555050841561184c57600183018590555b858260000160008282546118609190612631565b9091555050841561187357600182018590555b6040805180820190915282548152600183015460208201526118989088908390611996565b600183015460408051888152602081018790526001600160a01b038a16917f91ede45f04a37a7c170f5c1207df3b6bc748dc1e04ad5e917a241d0f52feada3910160405180910390a3851561198d576003546040516323b872dd60e01b81526001600160a01b03898116600483015230602483015260448201899052909116906323b872dd90606401602060405180830381600087803b15801561193b57600080fd5b505af115801561194f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119739190612391565b61198d57634e487b7160e01b600052600160045260246000fd5b50505050505050565b6000604051806080016040528060008152602001600081526020016000815260200160008152509050600060405180608001604052806000815260200160008152602001600081526020016000815250905060006040518060c0016040528060008152602001600081526020016008548152602001611a124390565b81526020014281526001600160a01b0388166000818152600d602090815260409091205492019190915290915015611b4f5780608001518560200151118015611a5b5750845115155b15611aa0578451611a7190630784ce0090612649565b83602001818152505080608001518560200151611a8e919061274a565b8360200151611a9d9190612669565b83525b80608001518460200151118015611ab75750835115155b15611afc578351611acd90630784ce0090612649565b82602001818152505080608001518460200151611aea919061274a565b8260200151611af99190612669565b82525b6020808601516000908152600e82526040902054825284015115611b4f57846020015184602001511415611b365780516020820152611b4f565b6020808501516000908152600e82526040902054908201525b60408051608080820183526000808352602083015283015181830152606080840151908201529082015115611bcb57600960008360400151815260200190815260200160002060405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505090505b604081015160808301518290600090831015611c285783604001518560800151611bf5919061274a565b84606001518660600151611c09919061274a565b611c1b90670de0b6b3a76400006126ec565b611c259190612649565b90505b600062093a80611c3881866126ec565b611c429190612649565b905060005b60ff8113611db957611c5c62093a8083612631565b915060008760800151831115611c785787608001519250611c89565b506000828152600e60205260409020545b611c93868461274a565b8760200151611ca29190612669565b87518890611cb190839061270b565b905250602087018051829190611cc89083906125f0565b905250865160001315611cda57600087525b600087602001511215611cef57600060208801525b60408088018490528501519295508592670de0b6b3a764000090611d13908561274a565b611d1d90866126ec565b611d279190612649565b8560600151611d369190612631565b606088015260408801805160019190611d50908390612631565b9052506080880151831415611d6f575060608088015190870152611db9565b604080890151600090815260096020908152908290208951815590890151600182015590880151600282015560608801516003909101555080611db18161279c565b915050611c47565b5060408601516008556001600160a01b038b1615611e3d5787602001518760200151611de5919061270b565b85602001818151611df691906125f0565b90525087518751611e07919061270b565b85518690611e169083906125f0565b905250602085015160001315611e2e57600060208601525b845160001315611e3d57600085525b604080870151600090815260096020908152908290208751815590870151600182015590860151600282015560608601516003909101556001600160a01b038b1615611fde5785608001518a602001511115611eea57602088015186518790611ea79083906125f0565b9052506020808b0151908a01511415611ed257602087015186518790611ece90839061270b565b9052505b85516020808c01516000908152600e90915260409020555b856080015189602001511115611f3e57896020015189602001511115611f3e57866020015186602001818151611f20919061270b565b9052506020808701518a8201516000908152600e9092526040909120555b6001600160a01b038b166000908152600d60205260408120805491611f62836127bc565b90915550506080860151604080890191909152606080880151908901526001600160a01b038c166000908152600c60205290812060a0880151899290611fa9906001612631565b815260208082019290925260409081016000208351815591830151600183015582015160028201556060909101516003909101555b5050505050505050505050565b60008082815b6080811361206c578183106120055761206c565b600060026120138486612631565b61201e906001612631565b6120289190612649565b600081815260096020526040902060030154909150871061204b57809350612059565b61205660018261274a565b92505b50806120648161279c565b915050611ff1565b5090949350505050565b60085460009081526009602090815260408083208151608081018352815481526001820154938101939093526002810154918301829052600301546060830152829062093a80906120c89082906126ec565b6120d29190612649565b905060005b60ff811361217e576120ec62093a8083612631565b915060008583111561210057859250612111565b506000828152600e60205260409020545b6040840151612120908461274a565b846020015161212f9190612669565b8451859061213e90839061270b565b9052508286141561214f575061217e565b808460200181815161216191906125f0565b9052505060408301829052806121768161279c565b9150506120d7565b5081516000131561218e57600082525b505192915050565b6001600160a01b0382166000908152600660209081526040918290208251808401909352805480845260018201549284019290925291906121e95760405162461bcd60e51b81526004016106a5906125c0565b6001600160a01b0380841660008181526006602090815260408083208151808301835281548152600182015481850152958a16808552600a90935281842080546001600160a01b031916861790559051909493927fef9fc1dee6010109e6e3b21e51d44028e246dbad8a5a71ea192a30b19e1f457f91a3600080855560408051808201909152908152600185015460208201526122899087908590611996565b82518254839060009061229d908490612631565b909155505060208101516122b657602083015160018301555b6040805180820190915282548152600183015460208201526122db9086908390611996565b505050505050565b6000602082840312156122f4578081fd5b81356122ff816127e2565b9392505050565b60008060408385031215612318578081fd5b8235612323816127e2565b946020939093013593505050565b60008060008060008060c08789031215612349578182fd5b8635612354816127e2565b95506020870135945060408701359350606087013560ff81168114612377578283fd5b9598949750929560808101359460a0909101359350915050565b6000602082840312156123a2578081fd5b815180151581146122ff578182fd5b6000602082840312156123c2578081fd5b5035919050565b600080604083850312156123db578182fd5b50508035926020909101359150565b600080835482600182811c91508083168061240657607f831692505b602080841082141561242657634e487b7160e01b87526022600452602487fd5b81801561243a576001811461244b57612477565b60ff19861689528489019650612477565b60008a815260209020885b8681101561246f5781548b820152908501908301612456565b505084890196505b509498975050505050505050565b6000602080835283518082850152825b818110156124b157858101830151858201604001528201612495565b818111156124c25783604083870101525b50601f01601f1916929092016040019392505050565b60208082526025908201527f536d61727420636f6e7472616374206465706f7369746f7273206e6f7420616c6040820152641b1bddd95960da1b606082015260800190565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b6020808252601c908201527f56616c7565206d7573742062652067726561746572207468616e203000000000604082015260600190565b6020808252600e908201526d1c99595b9d1c985b9d0818d85b1b60921b604082015260600190565b602080825260169082015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b604082015260600190565b600080821280156001600160ff1b0384900385131615612612576126126127cc565b600160ff1b839003841281161561262b5761262b6127cc565b50500190565b60008219821115612644576126446127cc565b500190565b60008261266457634e487b7160e01b81526012600452602481fd5b500490565b60006001600160ff1b038184138284138082168684048611161561268f5761268f6127cc565b600160ff1b848712828116878305891216156126ad576126ad6127cc565b8587129250878205871284841616156126c8576126c86127cc565b878505871281841616156126de576126de6127cc565b505050929093029392505050565b6000816000190483118215151615612706576127066127cc565b500290565b60008083128015600160ff1b850184121615612729576127296127cc565b6001600160ff1b0384018313811615612744576127446127cc565b50500390565b60008282101561275c5761275c6127cc565b500390565b600181811c9082168061277557607f821691505b6020821081141561279657634e487b7160e01b600052602260045260246000fd5b50919050565b60006001600160ff1b038214156127b5576127b56127cc565b5060010190565b60006000198214156127b5576127b55b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03811681146127f757600080fd5b5056fea264697066735822122059c998ce0904b57ba0eaf8e4884620d0bfa6c1235f4610d86b5a64c379a36c4664736f6c63430008040033