ETH Price: $2,868.20 (-2.75%)
 

Overview

Max Total Supply

214,425,463.596256925325020463 esALB

Holders

2,265 (0.00%)

Market

Price

$0.0132 @ 0.000005 ETH (-3.55%)

Onchain Market Cap

-

Circulating Supply Market Cap

$2,918,137.00

Other Info

Token Contract (WITH 18 Decimals)

Filtered by Token Holder
iloveyoublueworld.base.eth
Balance
186.729801234048581632 esALB

Value
$2.46 ( ~0.000857681978025358 ETH) [0.0001%]
0xDE2e4Fbb91763b16cC59c3A34d6AAAa521c61c0B
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

AlienBase is the Base-native DEX for degen earthlings.

Market

Volume (24H):$11,725.32
Market Capitalization:$2,918,137.00
Circulating Supply:221,161,950.00 esALB
Market Data Source: Coinmarketcap

Contract Source Code Verified (Exact Match)

Contract Name:
EsToken

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 800 runs

Other Settings:
default evmVersion
File 1 of 12 : EsToken.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;

import "@openzeppelin/contracts:uniswap/access/Ownable.sol";
import "@openzeppelin/contracts:uniswap/math/SafeMath.sol";
import "@openzeppelin/contracts:uniswap/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts:uniswap/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts:uniswap/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts:uniswap/utils/EnumerableSet.sol";
import "@openzeppelin/contracts:uniswap/token/ERC20/IERC20.sol";

import "./interfaces/tokens/IEsToken.sol";
import "./interfaces/IEsTokenUsage.sol";


/*
 * esToken is a generalized implementation of Camelot xGrail
 * It's non-transferable, except from/to whitelisted addresses
 * It can be converted back to Token through a vesting process
 * This contract is made to receive esToken deposits from users in order to allocate them to Usages (plugins) contracts
 * * Changes from original include:
 * - Removed deallocationFee
 * - Triggered redemptions have a time limit to avoid perpetual options to withdraw immediately while still earning yield
 * - Users can instantly redeem slightly more than 1% of their position every 12 hours
 * - Excess of underlyingTokens is sent to treasury instead of being burned
 * - Fixed bug in original contract where owner could set an invalid dividendsAddress and brick user funds
 */


contract EsToken is Ownable, ReentrancyGuard, ERC20, IEsToken {
  using Address for address;
  using SafeMath for uint256;
  using EnumerableSet for EnumerableSet.AddressSet;
  using SafeERC20 for IERC20;

  struct EsTokenBalance {
    uint256 allocatedAmount; // Amount of esToken allocated to a Usage
    uint256 redeemingAmount; // Total amount of esToken currently being redeemed
  }

  struct RedeemInfo {
    uint256 tokenAmount; // Token amount to receive when vesting has ended
    uint256 esTokenAmount; // esToken amount to redeem
    uint256 endTime;
    IEsTokenUsage dividendsAddress;
    uint256 dividendsAllocation; // Share of redeeming esToken to allocate to the Dividends Usage contract
  }

  IERC20 public immutable underlyingToken; // Token token to convert to/from
  IEsTokenUsage public dividendsAddress; // Farm/dividends contract

  EnumerableSet.AddressSet private _transferWhitelist; // addresses allowed to send/receive esToken

  mapping(address => mapping(address => uint256)) public usageApprovals; // Usage approvals to allocate esToken
  mapping(address => mapping(address => uint256)) public override usageAllocations; // Active esToken allocations to usages

  uint256 public constant MAX_FIXED_RATIO = 100; // 100%

  // Redeeming min/max settings
  uint256 public minRedeemRatio = 50; // 1:0.5
  uint256 public maxRedeemRatio = 100; // 1:1
  uint256 public minRedeemDuration = 15 days;
  uint256 public maxRedeemDuration = 30 days;
  // Adjusted dividends rewards for redeeming esToken
  uint256 public redeemDividendsAdjustment; // 0% at construction to avoid bricking calls
  
  uint256 public freeRedeemPercentage = 100; //1%
  //owner-defined minimum redemption value to avoid requiring infinite mini-redemptions to exit a position
  uint256 public minFreeRedeem;
  uint256 public freeRedeemCooldown = 12 hours;
  uint256 public redemptionGracePeriod = 7 days;

  mapping(address => EsTokenBalance) public esTokenBalances; // User's esToken balances
  mapping(address => RedeemInfo[]) public userRedeems; // User's redeeming instances
  mapping(address => uint256) public freeRedeemAllowances;

  address public treasuryAddress;


  constructor(IERC20 _underlyingToken, string memory name, string memory symbol, address _treasuryAddress) ERC20(name, symbol) {
    underlyingToken = _underlyingToken;
    _transferWhitelist.add(address(this));

    require(_treasuryAddress != address(0), "Invalid treasury address");

    treasuryAddress = _treasuryAddress;
  }

  /********************************************/
  /****************** EVENTS ******************/
  /********************************************/

  event ApproveUsage(address indexed userAddress, address indexed usageAddress, uint256 amount);
  event Convert(address indexed from, address to, uint256 amount);
  event UpdateRedeemSettings(
    uint256 minRedeemRatio,
    uint256 maxRedeemRatio,
    uint256 minRedeemDuration,
    uint256 maxRedeemDuration,
    uint256 redeemDividendsAdjustment,
    uint256 freeRedeemPercentage,
    uint256 minFreeRedeem,
    uint256 freeRedeemCooldown,
    uint256 redemptionGracePeriod
  );
  event UpdateDividendsAddress(address previousDividendsAddress, address newDividendsAddress);
  event SetTransferWhitelist(address account, bool add);
  event Redeem(address indexed userAddress, uint256 esTokenAmount, uint256 tokenAmount, uint256 duration);
  event FinalizeRedeem(address indexed userAddress, uint256 esTokenAmount, uint256 tokenAmount);
  event CancelRedeem(address indexed userAddress, uint256 esTokenAmount);
  event UpdateRedeemDividendsAddress(address indexed userAddress, uint256 redeemIndex, address previousDividendsAddress, address newDividendsAddress);
  event UpdateTreasuryAddress(address indexed newTreasuryAddress, address indexed oldTreasuryAddress);
  event Allocate(address indexed userAddress, address indexed usageAddress, uint256 amount);
  event Deallocate(address indexed userAddress, address indexed usageAddress, uint256 amount);

  /***********************************************/
  /****************** MODIFIERS ******************/
  /***********************************************/

  /*
   * @dev Check if a redeem entry exists
   */
  modifier validateRedeem(address userAddress, uint256 redeemIndex) {
    require(redeemIndex < userRedeems[userAddress].length, "validateRedeem: redeem entry does not exist");
    _;
  }

  /**************************************************/
  /****************** PUBLIC VIEWS ******************/
  /**************************************************/

  /*
   * @dev Returns user's esToken balances
   */
  function getEsTokenBalance(address userAddress) external view returns (uint256 allocatedAmount, uint256 redeemingAmount) {
    EsTokenBalance storage balance = esTokenBalances[userAddress];
    return (balance.allocatedAmount, balance.redeemingAmount);
  }

  /*
   * @dev returns redeemable Token for "amount" of esToken vested for "duration" seconds
   */
  function getTokenByVestingDuration(uint256 amount, uint256 duration) public view returns (uint256) {
    if(duration == 0) {
        return amount; //special case for small withdrawals
    }

    if(duration < minRedeemDuration) {
      return 0;
    }

    // capped to maxRedeemDuration
    if (duration > maxRedeemDuration) {
      return amount.mul(maxRedeemRatio).div(100);
    }

    uint256 ratio = minRedeemRatio.add(
      (duration.sub(minRedeemDuration)).mul(maxRedeemRatio.sub(minRedeemRatio))
      .div(maxRedeemDuration.sub(minRedeemDuration))
    );

    return amount.mul(ratio).div(100);
  }

  /**
   * @dev returns quantity of "userAddress" pending redeems
   */
  function getUserRedeemsLength(address userAddress) external view returns (uint256) {
    return userRedeems[userAddress].length;
  }

  /**
   * @dev returns "userAddress" info for a pending redeem identified by "redeemIndex"
   */
  function getUserRedeem(address userAddress, uint256 redeemIndex) external view validateRedeem(userAddress, redeemIndex) returns (uint256 tokenAmount, uint256 esTokenAmount, uint256 endTime, address dividendsContract, uint256 dividendsAllocation) {
    RedeemInfo storage _redeem = userRedeems[userAddress][redeemIndex];
    return (_redeem.tokenAmount, _redeem.esTokenAmount, _redeem.endTime, address(_redeem.dividendsAddress), _redeem.dividendsAllocation);
  }

  /**
   * @dev returns approved esToken to allocate from "userAddress" to "usageAddress"
   */
  function getUsageApproval(address userAddress, address usageAddress) external view returns (uint256) {
    return usageApprovals[userAddress][usageAddress];
  }

  /**
   * @dev returns allocated esToken from "userAddress" to "usageAddress"
   */
  function getUsageAllocation(address userAddress, address usageAddress) external view returns (uint256) {
    return usageAllocations[userAddress][usageAddress];
  }

  /**
   * @dev returns length of transferWhitelist array
   */
  function transferWhitelistLength() external view returns (uint256) {
    return _transferWhitelist.length();
  }

  /**
   * @dev returns transferWhitelist array item's address for "index"
   */
  function transferWhitelist(uint256 index) external view returns (address) {
    return _transferWhitelist.at(index);
  }

  /**
   * @dev returns if "account" is allowed to send/receive esToken
   */
  function isTransferWhitelisted(address account) external override view returns (bool) {
    return _transferWhitelist.contains(account);
  }

  /*******************************************************/
  /****************** OWNABLE FUNCTIONS ******************/
  /*******************************************************/

  /**
   * @dev Updates all redeem ratios and durations
   *
   * Must only be called by owner
   */
  function updateRedeemSettings(
    uint256 minRedeemRatio_,
    uint256 maxRedeemRatio_,
    uint256 minRedeemDuration_,
    uint256 maxRedeemDuration_,
    uint256 redeemDividendsAdjustment_,
    uint256 freeRedeemPercentage_,
    uint256 minFreeRedeem_,
    uint256 freeRedeemCooldown_,
    uint256 redemptionGracePeriod_
    ) external onlyOwner {
    require(minRedeemRatio_ <= maxRedeemRatio_, "updateRedeemSettings: wrong ratio values");
    require(minRedeemDuration_ < maxRedeemDuration_, "updateRedeemSettings: wrong duration values");
    // should never exceed 100%
    require(maxRedeemRatio_ <= MAX_FIXED_RATIO && redeemDividendsAdjustment_ <= MAX_FIXED_RATIO, "updateRedeemSettings: wrong ratio values");
    if(redeemDividendsAdjustment_ > 0) {
      require(address(dividendsAddress) != address(0), "updateRedeemSettings: dividendsAddress not initialized");
      //tests an allocation to ensure the target contract responds to specification
      dividendsAddress.allocate(address(this), 0, new bytes(0));
      dividendsAddress.deallocate(address(this), 0, new bytes(0));
    }

    require(freeRedeemPercentage_ >= 0 && freeRedeemPercentage_ <= 10000, "updateRedeemSettings: invalid redeem percentage");
    
    minRedeemRatio = minRedeemRatio_;
    maxRedeemRatio = maxRedeemRatio_;
    minRedeemDuration = minRedeemDuration_;
    maxRedeemDuration = maxRedeemDuration_;
    redeemDividendsAdjustment = redeemDividendsAdjustment_;

    freeRedeemPercentage = freeRedeemPercentage_;
    minFreeRedeem = minFreeRedeem_;
    freeRedeemCooldown = freeRedeemCooldown_;
    redemptionGracePeriod = redemptionGracePeriod_;
    

    emit UpdateRedeemSettings(
        minRedeemRatio_,
        maxRedeemRatio_,
        minRedeemDuration_,
        maxRedeemDuration_,
        redeemDividendsAdjustment_,
        freeRedeemPercentage_,
        minFreeRedeem_,
        freeRedeemCooldown_,
        redemptionGracePeriod_
    );
  }

  function updateTreasuryAddress(address _newTreasuryAddress) external onlyOwner {
    require(_newTreasuryAddress != address(0), "Invalid treasury address");
    emit UpdateTreasuryAddress(_newTreasuryAddress, treasuryAddress);
    treasuryAddress = _newTreasuryAddress;
  }

  /**
   * @dev Updates dividends contract address
   *
   * Must only be called by owner
   */
  function updateDividendsAddress(IEsTokenUsage dividendsAddress_) external onlyOwner {
    // if set to 0, also set divs earnings while redeeming to 0
    if(address(dividendsAddress_) == address(0)) {
      redeemDividendsAdjustment = 0;
    } else {
      //tests an allocation to ensure the target contract responds to specification
      dividendsAddress_.allocate(address(this), 0, new bytes(0));
      dividendsAddress_.deallocate(address(this), 0, new bytes(0));
    }

    emit UpdateDividendsAddress(address(dividendsAddress), address(dividendsAddress_));
    dividendsAddress = dividendsAddress_;
  }


  /**
   * @dev Adds or removes addresses from the transferWhitelist
   */
  function updateTransferWhitelist(address account, bool add) external onlyOwner {
    require(account != address(this), "updateTransferWhitelist: Cannot remove esToken from whitelist");

    if(add) _transferWhitelist.add(account);
    else _transferWhitelist.remove(account);

    emit SetTransferWhitelist(account, add);
  }

  /*****************************************************************/
  /******************  EXTERNAL PUBLIC FUNCTIONS  ******************/
  /*****************************************************************/

  /**
   * @dev Approves "usage" address to get allocations up to "amount" of esToken from msg.sender
   */
  function approveUsage(IEsTokenUsage usage, uint256 amount) external nonReentrant {
    require(address(usage) != address(0), "approveUsage: approve to the zero address");

    usageApprovals[msg.sender][address(usage)] = amount;
    emit ApproveUsage(msg.sender, address(usage), amount);
  }

  /**
   * @dev Convert caller's "amount" of Token to esToken
   */
  function convert(uint256 amount) external nonReentrant {
    _convert(amount, msg.sender);
  }

  /**
   * @dev Convert caller's "amount" of Token to esToken to "to" address
   */
  function convertTo(uint256 amount, address to) external override nonReentrant {
    require(address(msg.sender).isContract(), "convertTo: not allowed");
    _convert(amount, to);
  }

  /**
   * @dev Initiates redeem process (esToken to Token)
   *
   * Handles dividends' compensation allocation during the vesting process if needed
   */
  function redeem(uint256 esTokenAmount, uint256 duration) external nonReentrant {
    require(esTokenAmount > 0, "redeem: esTokenAmount cannot be null");

    EsTokenBalance storage balance = esTokenBalances[msg.sender];
    {
        //instant free redeem check
        //use total balance for percentages
        //call reverts if user tries to redeem more than the free balance
        uint256 lastUserRedemption = freeRedeemAllowances[msg.sender];
        uint256 userTotalBalance = super.balanceOf(msg.sender).add(balance.allocatedAmount).add(balance.redeemingAmount);


        uint256 freeAllowance = (userTotalBalance.mul(freeRedeemPercentage) / 10000).add(minFreeRedeem);
        
        if( duration != 0 
        || _currentBlockTimestamp() < lastUserRedemption + freeRedeemCooldown 
        || esTokenAmount > freeAllowance) {
            require(duration >= minRedeemDuration, "redeem: invalid request");
        }
    }    

    _transfer(msg.sender, address(this), esTokenAmount);

    // get corresponding Token amount
    uint256 tokenAmount = getTokenByVestingDuration(esTokenAmount, duration);
    emit Redeem(msg.sender, esTokenAmount, tokenAmount, duration);

    // if redeeming is not immediate, go through vesting process
    if(duration > 0) {
        // add to SBT total
        balance.redeemingAmount = balance.redeemingAmount.add(esTokenAmount);

        // handle dividends during the vesting process
        uint256 dividendsAllocation = esTokenAmount.mul(redeemDividendsAdjustment).div(100);
        // only if compensation is active
        if(dividendsAllocation > 0) {
            // allocate to dividends
            dividendsAddress.allocate(msg.sender, dividendsAllocation, new bytes(0));
        }

        // add redeeming entry
        userRedeems[msg.sender].push(RedeemInfo(tokenAmount, esTokenAmount, _currentBlockTimestamp().add(duration), dividendsAddress, dividendsAllocation));
    } else {
        //updates timestamp for cooldown
        freeRedeemAllowances[msg.sender] = _currentBlockTimestamp();
        // immediately redeem for underlyingToken
        _finalizeRedeem(msg.sender, esTokenAmount, tokenAmount);
    }
  }

  /**
   * @dev Finalizes redeem process when vesting duration has been reached
   *
   * Can only be called by the redeem entry owner
   */
  function finalizeRedeem(uint256 redeemIndex) external nonReentrant validateRedeem(msg.sender, redeemIndex) {
    EsTokenBalance storage balance = esTokenBalances[msg.sender];
    RedeemInfo storage _redeem = userRedeems[msg.sender][redeemIndex];
    require(_currentBlockTimestamp() >= _redeem.endTime, "finalizeRedeem: vesting duration has not ended yet");
    //Introduce a limit to redemptions to avoid giving free options. Users must cancel and restart redemption
    require(_currentBlockTimestamp() <= _redeem.endTime + redemptionGracePeriod, "finalizeRedeem: grace period expired");

    // remove from SBT total
    balance.redeemingAmount = balance.redeemingAmount.sub(_redeem.esTokenAmount);
    _finalizeRedeem(msg.sender, _redeem.esTokenAmount, _redeem.tokenAmount);

    // handle dividends compensation if any was active
    if(_redeem.dividendsAllocation > 0) {
      // deallocate from dividends
      IEsTokenUsage(_redeem.dividendsAddress).deallocate(msg.sender, _redeem.dividendsAllocation, new bytes(0));
    }

    // remove redeem entry
    _deleteRedeemEntry(redeemIndex);
  }

  /**
   * @dev Updates dividends address for an existing active redeeming process
   *
   * Can only be called by the involved user
   * Should only be used if dividends contract was to be migrated
   */
  function updateRedeemDividendsAddress(uint256 redeemIndex) external nonReentrant validateRedeem(msg.sender, redeemIndex) {
    RedeemInfo storage _redeem = userRedeems[msg.sender][redeemIndex];

    // only if the active dividends contract is not the same anymore
    if(dividendsAddress != _redeem.dividendsAddress && address(dividendsAddress) != address(0)) {
      if(_redeem.dividendsAllocation > 0) {
        // deallocate from old dividends contract
        _redeem.dividendsAddress.deallocate(msg.sender, _redeem.dividendsAllocation, new bytes(0));
        // allocate to new used dividends contract
        dividendsAddress.allocate(msg.sender, _redeem.dividendsAllocation, new bytes(0));
      }

      emit UpdateRedeemDividendsAddress(msg.sender, redeemIndex, address(_redeem.dividendsAddress), address(dividendsAddress));
      _redeem.dividendsAddress = dividendsAddress;
    }
  }

  /**
   * @dev Cancels an ongoing redeem entry
   *
   * Can only be called by its owner
   */
  function cancelRedeem(uint256 redeemIndex) external nonReentrant validateRedeem(msg.sender, redeemIndex) {
    EsTokenBalance storage balance = esTokenBalances[msg.sender];
    RedeemInfo storage _redeem = userRedeems[msg.sender][redeemIndex];

    // make redeeming esToken available again
    balance.redeemingAmount = balance.redeemingAmount.sub(_redeem.esTokenAmount);
    _transfer(address(this), msg.sender, _redeem.esTokenAmount);

    // handle dividends compensation if any was active
    if(_redeem.dividendsAllocation > 0) {
      // deallocate from dividends
      IEsTokenUsage(_redeem.dividendsAddress).deallocate(msg.sender, _redeem.dividendsAllocation, new bytes(0));
    }

    emit CancelRedeem(msg.sender, _redeem.esTokenAmount);

    // remove redeem entry
    _deleteRedeemEntry(redeemIndex);
  }


  /**
   * @dev Allocates caller's "amount" of available esToken to "usageAddress" contract
   *
   * args specific to usage contract must be passed into "usageData"
   */
  function allocate(address usageAddress, uint256 amount, bytes calldata usageData) external nonReentrant {
    _allocate(msg.sender, usageAddress, amount);

    // allocates esToken to usageContract
    IEsTokenUsage(usageAddress).allocate(msg.sender, amount, usageData);
  }

  /**
   * @dev Allocates "amount" of available esToken from "userAddress" to caller (ie usage contract)
   *
   * Caller must have an allocation approval for the required esToken from "userAddress"
   */
  function allocateFromUsage(address userAddress, uint256 amount) external override nonReentrant {
    _allocate(userAddress, msg.sender, amount);
  }

  /**
   * @dev Deallocates caller's "amount" of available esToken from "usageAddress" contract
   *
   * args specific to usage contract must be passed into "usageData"
   */
  function deallocate(address usageAddress, uint256 amount, bytes calldata usageData) external nonReentrant {
    _deallocate(msg.sender, usageAddress, amount);

    // deallocate esToken into usageContract
    IEsTokenUsage(usageAddress).deallocate(msg.sender, amount, usageData);
  }

  /**
   * @dev Deallocates "amount" of allocated esToken belonging to "userAddress" from caller (ie usage contract)
   *
   * Caller can only deallocate esToken from itself
   */
  function deallocateFromUsage(address userAddress, uint256 amount) external override nonReentrant {
    _deallocate(userAddress, msg.sender, amount);
  }

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

  /**
   * @dev Convert caller's "amount" of Token into esToken to "to"
   */
  function _convert(uint256 amount, address to) internal {
    require(amount != 0, "convert: amount cannot be null");

    // mint new esToken
    _mint(to, amount);

    emit Convert(msg.sender, to, amount);
    underlyingToken.safeTransferFrom(msg.sender, address(this), amount);
  }

  /**
   * @dev Finalizes the redeeming process for "userAddress" by transferring him "tokenAmount" and removing "esTokenAmount" from supply
   *
   * Any vesting check should be ran before calling this
   * Token excess is automatically burnt
   */
  function _finalizeRedeem(address userAddress, uint256 esTokenAmount, uint256 tokenAmount) internal {
    uint256 tokenExcess = esTokenAmount.sub(tokenAmount);

    // sends due tokens
    underlyingToken.safeTransfer(userAddress, tokenAmount);

    // sends excess tokens to treasury
    if(tokenExcess > 0) {
      underlyingToken.safeTransfer(treasuryAddress, tokenExcess);
    }
    _burn(address(this), esTokenAmount);

    emit FinalizeRedeem(userAddress, esTokenAmount, tokenAmount);
  }

  /**
   * @dev Allocates "userAddress" user's "amount" of available esToken to "usageAddress" contract
   *
   */
  function _allocate(address userAddress, address usageAddress, uint256 amount) internal {
    require(amount > 0, "allocate: amount cannot be null");

    EsTokenBalance storage balance = esTokenBalances[userAddress];

    // approval checks if allocation request amount has been approved by userAddress to be allocated to this usageAddress
    uint256 approvedEsToken = usageApprovals[userAddress][usageAddress];
    require(approvedEsToken >= amount, "allocate: non authorized amount");

    // remove allocated amount from usage's approved amount
    usageApprovals[userAddress][usageAddress] = approvedEsToken.sub(amount);

    // update usage's allocatedAmount for userAddress
    usageAllocations[userAddress][usageAddress] = usageAllocations[userAddress][usageAddress].add(amount);

    // adjust user's esToken balances
    balance.allocatedAmount = balance.allocatedAmount.add(amount);
    _transfer(userAddress, address(this), amount);

    emit Allocate(userAddress, usageAddress, amount);
  }

  /**
   * @dev Deallocates "amount" of available esToken to "usageAddress" contract
   *
   * args specific to usage contract must be passed into "usageData"
   */
  function _deallocate(address userAddress, address usageAddress, uint256 amount) internal {
    require(amount > 0, "deallocate: amount cannot be null");

    // check if there is enough allocated esToken to this usage to deallocate
    uint256 allocatedAmount = usageAllocations[userAddress][usageAddress];
    require(allocatedAmount >= amount, "deallocate: non authorized amount");

    // remove deallocated amount from usage's allocation
    usageAllocations[userAddress][usageAddress] = allocatedAmount.sub(amount);

    // adjust user's esToken balances
    EsTokenBalance storage balance = esTokenBalances[userAddress];
    balance.allocatedAmount = balance.allocatedAmount.sub(amount);
    _transfer(address(this), userAddress, amount);
    
    emit Deallocate(userAddress, usageAddress, amount);
  }

  function _deleteRedeemEntry(uint256 index) internal {
    userRedeems[msg.sender][index] = userRedeems[msg.sender][userRedeems[msg.sender].length - 1];
    userRedeems[msg.sender].pop();
  }

  /**
   * @dev Hook override to forbid transfers except from whitelisted addresses and minting
   */
  function _beforeTokenTransfer(address from, address to, uint256 /*amount*/) internal view override {
    require(from == address(0) || _transferWhitelist.contains(from) || _transferWhitelist.contains(to), "transfer: not allowed");
  }

  /**
   * @dev Utility function to get the current block timestamp
   */
  function _currentBlockTimestamp() internal view virtual returns (uint256) {
    /* solhint-disable not-rely-on-time */
    return block.timestamp;
  }

}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.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 () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), 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 {
        emit OwnershipTransferred(_owner, address(0));
        _owner = 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");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

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

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

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

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

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

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 10 of 12 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.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 make 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;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;

interface IEsTokenUsage {
    function allocate(address userAddress, uint256 amount, bytes calldata data) external;
    function deallocate(address userAddress, uint256 amount, bytes calldata data) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;

import "@openzeppelin/contracts:uniswap/token/ERC20/IERC20.sol";

interface IEsToken is IERC20 {
  function usageAllocations(address userAddress, address usageAddress) external view returns (uint256 allocation);

  function allocateFromUsage(address userAddress, uint256 amount) external;
  function convertTo(uint256 amount, address to) external;
  function deallocateFromUsage(address userAddress, uint256 amount) external;

  function isTransferWhitelisted(address account) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IERC20","name":"_underlyingToken","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"_treasuryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":true,"internalType":"address","name":"usageAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Allocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":true,"internalType":"address","name":"usageAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApproveUsage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"esTokenAmount","type":"uint256"}],"name":"CancelRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Convert","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":true,"internalType":"address","name":"usageAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deallocate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"esTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"FinalizeRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"esTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"add","type":"bool"}],"name":"SetTransferWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousDividendsAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newDividendsAddress","type":"address"}],"name":"UpdateDividendsAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"previousDividendsAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newDividendsAddress","type":"address"}],"name":"UpdateRedeemDividendsAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minRedeemRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRedeemRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minRedeemDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRedeemDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemDividendsAdjustment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freeRedeemPercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minFreeRedeem","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freeRedeemCooldown","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redemptionGracePeriod","type":"uint256"}],"name":"UpdateRedeemSettings","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newTreasuryAddress","type":"address"},{"indexed":true,"internalType":"address","name":"oldTreasuryAddress","type":"address"}],"name":"UpdateTreasuryAddress","type":"event"},{"inputs":[],"name":"MAX_FIXED_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"usageAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"usageData","type":"bytes"}],"name":"allocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"allocateFromUsage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IEsTokenUsage","name":"usage","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveUsage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"cancelRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"convertTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"usageAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"usageData","type":"bytes"}],"name":"deallocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deallocateFromUsage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dividendsAddress","outputs":[{"internalType":"contract IEsTokenUsage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"esTokenBalances","outputs":[{"internalType":"uint256","name":"allocatedAmount","type":"uint256"},{"internalType":"uint256","name":"redeemingAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"finalizeRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"freeRedeemAllowances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeRedeemCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeRedeemPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getEsTokenBalance","outputs":[{"internalType":"uint256","name":"allocatedAmount","type":"uint256"},{"internalType":"uint256","name":"redeemingAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"getTokenByVestingDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"address","name":"usageAddress","type":"address"}],"name":"getUsageAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"address","name":"usageAddress","type":"address"}],"name":"getUsageApproval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"getUserRedeem","outputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"esTokenAmount","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address","name":"dividendsContract","type":"address"},{"internalType":"uint256","name":"dividendsAllocation","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserRedeemsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isTransferWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeemDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeemRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minFreeRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minRedeemDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minRedeemRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"esTokenAmount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemDividendsAdjustment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redemptionGracePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"transferWhitelist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferWhitelistLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IEsTokenUsage","name":"dividendsAddress_","type":"address"}],"name":"updateDividendsAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemIndex","type":"uint256"}],"name":"updateRedeemDividendsAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minRedeemRatio_","type":"uint256"},{"internalType":"uint256","name":"maxRedeemRatio_","type":"uint256"},{"internalType":"uint256","name":"minRedeemDuration_","type":"uint256"},{"internalType":"uint256","name":"maxRedeemDuration_","type":"uint256"},{"internalType":"uint256","name":"redeemDividendsAdjustment_","type":"uint256"},{"internalType":"uint256","name":"freeRedeemPercentage_","type":"uint256"},{"internalType":"uint256","name":"minFreeRedeem_","type":"uint256"},{"internalType":"uint256","name":"freeRedeemCooldown_","type":"uint256"},{"internalType":"uint256","name":"redemptionGracePeriod_","type":"uint256"}],"name":"updateRedeemSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"add","type":"bool"}],"name":"updateTransferWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasuryAddress","type":"address"}],"name":"updateTreasuryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"usageAllocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"usageApprovals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userRedeems","outputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"esTokenAmount","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"contract IEsTokenUsage","name":"dividendsAddress","type":"address"},{"internalType":"uint256","name":"dividendsAllocation","type":"uint256"}],"stateMutability":"view","type":"function"}]

60a06040526032600c556064600d556213c680600e5562278d00600f55606460115561a8c060135562093a806014553480156200003b57600080fd5b506040516200485b3803806200485b833981810160405260808110156200006157600080fd5b8151602083018051604051929492938301929190846401000000008211156200008957600080fd5b9083019060208201858111156200009f57600080fd5b8251640100000000811182820188101715620000ba57600080fd5b82525081516020918201929091019080838360005b83811015620000e9578181015183820152602001620000cf565b50505050905090810190601f168015620001175780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200013b57600080fd5b9083019060208201858111156200015157600080fd5b82516401000000008111828201881017156200016c57600080fd5b82525081516020918201929091019080838360005b838110156200019b57818101518382015260200162000181565b50505050905090810190601f168015620001c95780820380516001836020036101000a031916815260200191505b50604052602001519150839050826000620001e362000320565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35060018055815162000246906005906020850190620003ab565b5080516200025c906006906020840190620003ab565b505060078054601260ff1990911617905550606084901b6001600160601b0319166080526200029960083062000324602090811b62002e3317901c565b506001600160a01b038116620002f6576040805162461bcd60e51b815260206004820152601860248201527f496e76616c696420747265617375727920616464726573730000000000000000604482015290519081900360640190fd5b601880546001600160a01b0319166001600160a01b03929092169190911790555062000457915050565b3390565b60006200033b836001600160a01b03841662000344565b90505b92915050565b600062000352838362000393565b6200038a575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200033e565b5060006200033e565b60009081526001919091016020526040902054151590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620003e357600085556200042e565b82601f10620003fe57805160ff19168380011785556200042e565b828001600101855582156200042e579182015b828111156200042e57825182559160200191906001019062000411565b506200043c92915062000440565b5090565b5b808211156200043c576000815560010162000441565b60805160601c6143d762000484600039806110ff52806136db52806137e6528061382452506143d76000f3fe608060405234801561001057600080fd5b50600436106103785760003560e01c8063619ac95b116101d3578063a3908e1b11610104578063c5f956af116100a2578063dd62ed3e1161007c578063dd62ed3e14610b5d578063e3a2950b14610b8b578063e9ed87f814610b93578063f2fde38b14610b9b57610378565b8063c5f956af14610b21578063cc6c542314610b29578063d402bfd314610b5557610378565b8063aff6cbf1116100de578063aff6cbf114610aaa578063b90c2b5214610ac7578063c360ed1c14610aed578063c4b1076614610b1957610378565b8063a3908e1b14610a35578063a457c2d714610a52578063a9059cbb14610a7e57610378565b8063841e4561116101715780638da5cb5b1161014b5780638da5cb5b146109f157806395d89b41146109f9578063a0bdc7cb14610a01578063a303203914610a2d57610378565b8063841e45611461097757806388adb48d1461099d57806389083654146109c357610378565b80637afff699116101ad5780637afff699146108bc5780637cbc2373146108e25780637f10c94b14610905578063806af0b41461095457610378565b8063619ac95b1461088657806370a082311461088e578063715018a6146108b457610378565b8063313ce567116102ad5780634a5b406e1161024b578063539ffb7711610225578063539ffb7714610779578063549230c9146107965780635a1d34dc1461081b5780635cdd175f1461084757610378565b80634a5b406e146106f45780634b359d38146106fc5780634f62b7ec1461071957610378565b80633b90f9a0116102875780633b90f9a01461068a5780633c0546b7146106b6578063488c8303146106be578063497965ee146106ec57610378565b8063313ce567146106385780633862e21a14610656578063395093511461065e57610378565b80631eee7e601161031a5780632b489679116102f45780632b489679146105915780632cc2f5ce146105bf5780632e9a76e4146105ed57806331124ce31461061b57610378565b80631eee7e601461051157806323b872dd146105375780632495a5991461056d57610378565b8063161aab4311610356578063161aab431461046257806318160ddd1461047c5780631c352679146104845780631c75e3691461048c57610378565b806306045a211461037d57806306fdde03146103a5578063095ea7b314610422575b600080fd5b6103a36004803603602081101561039357600080fd5b50356001600160a01b0316610bc1565b005b6103ad610e94565b6040805160208082528351818301528351919283929083019185019080838360005b838110156103e75781810151838201526020016103cf565b50505050905090810190601f1680156104145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61044e6004803603604081101561043857600080fd5b506001600160a01b038135169060200135610f2a565b604080519115158252519081900360200190f35b61046a610f48565b60408051918252519081900360200190f35b61046a610f59565b61046a610f5f565b6103a3600480360360608110156104a257600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156104d257600080fd5b8201836020820111156104e457600080fd5b8035906020019184600183028401116401000000008311171561050657600080fd5b509092509050610f65565b61044e6004803603602081101561052757600080fd5b50356001600160a01b0316611068565b61044e6004803603606081101561054d57600080fd5b506001600160a01b03813581169160208101359091169060400135611075565b6105756110fd565b604080516001600160a01b039092168252519081900360200190f35b61046a600480360360408110156105a757600080fd5b506001600160a01b0381358116916020013516611121565b61046a600480360360408110156105d557600080fd5b506001600160a01b038135811691602001351661114c565b61046a6004803603604081101561060357600080fd5b506001600160a01b0381358116916020013516611169565b6103a36004803603602081101561063157600080fd5b5035611194565b61064061150d565b6040805160ff9092168252519081900360200190f35b61046a611516565b61044e6004803603604081101561067457600080fd5b506001600160a01b03813516906020013561151c565b6103a3600480360360408110156106a057600080fd5b506001600160a01b03813516906020013561156a565b61046a6115c8565b61046a600480360360408110156106d457600080fd5b506001600160a01b03813581169160200135166115ce565b6105756115eb565b61046a6115ff565b6105756004803603602081101561071257600080fd5b5035611605565b6107456004803603604081101561072f57600080fd5b506001600160a01b038135169060200135611612565b604080519586526020860194909452848401929092526001600160a01b031660608401526080830152519081900360a00190f35b6103a36004803603602081101561078f57600080fd5b5035611669565b6103a3600480360360608110156107ac57600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156107dc57600080fd5b8201836020820111156107ee57600080fd5b8035906020019184600183028401116401000000008311171561081057600080fd5b5090925090506118b3565b6103a36004803603604081101561083157600080fd5b50803590602001356001600160a01b0316611994565b61086d6004803603602081101561085d57600080fd5b50356001600160a01b0316611a43565b6040805192835260208301919091528051918290030190f35b61046a611a5c565b61046a600480360360208110156108a457600080fd5b50356001600160a01b0316611a61565b6103a3611a7c565b61046a600480360360208110156108d257600080fd5b50356001600160a01b0316611b3a565b6103a3600480360360408110156108f857600080fd5b5080359060200135611b4c565b6103a3600480360361012081101561091c57600080fd5b5080359060208101359060408101359060608101359060808101359060a08101359060c08101359060e0810135906101000135611f47565b61046a6004803603604081101561096a57600080fd5b5080359060200135612381565b6103a36004803603602081101561098d57600080fd5b50356001600160a01b031661243b565b61086d600480360360208110156109b357600080fd5b50356001600160a01b0316612567565b6103a3600480360360408110156109d957600080fd5b506001600160a01b038135169060200135151561258a565b6105756126b3565b6103ad6126c2565b6103a360048036036040811015610a1757600080fd5b506001600160a01b038135169060200135612723565b61046a612779565b6103a360048036036020811015610a4b57600080fd5b503561277f565b61044e60048036036040811015610a6857600080fd5b506001600160a01b0381351690602001356127db565b61044e60048036036040811015610a9457600080fd5b506001600160a01b038135169060200135612843565b6103a360048036036020811015610ac057600080fd5b5035612857565b61046a60048036036020811015610add57600080fd5b50356001600160a01b0316612af0565b6103a360048036036040811015610b0357600080fd5b506001600160a01b038135169060200135612b0b565b61046a612c01565b610575612c07565b61074560048036036040811015610b3f57600080fd5b506001600160a01b038135169060200135612c16565b61046a612ce2565b61046a60048036036040811015610b7357600080fd5b506001600160a01b0381358116916020013516612ce8565b61046a612d13565b61046a612d19565b6103a360048036036020811015610bb157600080fd5b50356001600160a01b0316612d1f565b610bc9612e48565b6001600160a01b0316610bda6126b3565b6001600160a01b031614610c35576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610c4d576000601055610e08565b6040805160008082526020820192839052631c75e36960e01b83523060248301818152604484018390526060606485019081528451608486018190526001600160a01b03881696631c75e3699694959493929160a485019190808383895b83811015610cc3578181015183820152602001610cab565b50505050905090810190601f168015610cf05780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015610d1157600080fd5b505af1158015610d25573d6000803e3d6000fd5b5050604080516000808252602082019283905263549230c960e01b83523060248301818152604484018390526060606485019081528451608486018190526001600160a01b038a16985063549230c9975092959394939192909160a4850191808383895b83811015610da1578181015183820152602001610d89565b50505050905090810190601f168015610dce5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015610def57600080fd5b505af1158015610e03573d6000803e3d6000fd5b505050505b600754604080516001600160a01b0361010090930483168152918316602083015280517f044c75b8fa43ce72364b4c23fdb8451beafbda46505bf44c76f0853a01ed4ade9281900390910190a1600780546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610f205780601f10610ef557610100808354040283529160200191610f20565b820191906000526020600020905b815481529060010190602001808311610f0357829003601f168201915b5050505050905090565b6000610f3e610f37612e48565b8484612e4c565b5060015b92915050565b6000610f546008612f38565b905090565b60045490565b600c5481565b60026001541415610fab576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b6002600155610fbb338585612f43565b836001600160a01b0316631c75e369338585856040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505060018055505050505050565b6000610f426008836130ff565b6000611082848484613114565b6110f28461108e612e48565b6110ed85604051806060016040528060288152602001614216602891396001600160a01b038a166000908152600360205260408120906110cc612e48565b6001600160a01b031681526020810191909152604001600020549190613271565b612e4c565b5060015b9392505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205490565b600b60209081526000928352604080842090915290825290205481565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205490565b600260015414156111da576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b6002600155336000818152601660205260409020548290811061122e5760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b33600090815260166020526040812080548590811061124957fe5b6000918252602090912060059091020160038101546007549192506001600160a01b039081166101009092041614801590611293575060075461010090046001600160a01b031615155b156115035760048101541561147d5760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b83811015611329578181015183820152602001611311565b50505050905090810190601f1680156113565780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561137757600080fd5b505af115801561138b573d6000803e3d6000fd5b505060075460048401546040805160008082526020820192839052631c75e36960e01b83523360248301818152604484018690526060606485019081528451608486018190526101009098046001600160a01b03169950631c75e3699850919693949093919260a4860192908190849084905b838110156114165781810151838201526020016113fe565b50505050905090810190601f1680156114435780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561146457600080fd5b505af1158015611478573d6000803e3d6000fd5b505050505b6003810154600754604080518781526001600160a01b03938416602082015261010090920490921681830152905133917fa60c8f9118be22c9277a8129333d64ffda3de44ca7a5831d077a3127f1237a18919081900360600190a26007546003820180546101009092046001600160a01b03166001600160a01b03199092169190911790555b5050600180555050565b60075460ff1690565b60125481565b6000610f3e611529612e48565b846110ed856003600061153a612e48565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490613308565b600260015414156115b0576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556115c0823383612f43565b505060018055565b60135481565b600a60209081526000928352604080842090915290825290205481565b60075461010090046001600160a01b031681565b60105481565b6000610f42600883613362565b6016602052816000526040600020818154811061162e57600080fd5b600091825260209091206005909102018054600182015460028301546003840154600490940154929550909350916001600160a01b03169085565b600260015414156116af576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015533600081815260166020526040902054829081106117035760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b3360009081526015602090815260408083206016909252822080549192918690811061172b57fe5b906000526020600020906005020190506117568160010154836001015461336e90919063ffffffff16565b826001018190555061176d30338360010154613114565b6004810154156118655760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b838110156117fe5781810151838201526020016117e6565b50505050905090810190601f16801561182b5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561184c57600080fd5b505af1158015611860573d6000803e3d6000fd5b505050505b6001810154604080519182525133917f56d7520e387607a8daa892e3fed116badc2a636307bdc794b1c1aed97ae203f4919081900360200190a26118a8856133cb565b505060018055505050565b600260015414156118f9576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556119093385856134db565b836001600160a01b031663549230c9338585856040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561104657600080fd5b600260015414156119da576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556119e833613629565b611a39576040805162461bcd60e51b815260206004820152601660248201527f636f6e76657274546f3a206e6f7420616c6c6f77656400000000000000000000604482015290519081900360640190fd5b6115c0828261362f565b6015602052600090815260409020805460019091015482565b606481565b6001600160a01b031660009081526002602052604090205490565b611a84612e48565b6001600160a01b0316611a956126b3565b6001600160a01b031614611af0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60176020526000908152604090205481565b60026001541415611b92576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015581611bd35760405162461bcd60e51b81526004018080602001828103825260248152602001806143266024913960400191505060405180910390fd5b3360008181526015602090815260408083206017909252822054600182015482549294919392611c0f92611c0991908290611a61565b90613308565b90506000611c3f601254612710611c316011548661370790919063ffffffff16565b81611c3857fe5b0490613308565b905084151580611c5957506013548301611c57613760565b105b80611c6357508086115b15611cbf57600e54851015611cbf576040805162461bcd60e51b815260206004820152601760248201527f72656465656d3a20696e76616c69642072657175657374000000000000000000604482015290519081900360640190fd5b505050611ccd333085613114565b6000611cd98484612381565b6040805186815260208101839052808201869052905191925033917fbd5034ffbd47e4e72a94baa2cdb74c6fad73cb3bcdc13036b72ec8306f5a76469181900360600190a28215611f21576001820154611d339085613308565b6001830155601054600090611d5690606490611d50908890613707565b90613764565b90508015611e6b5760075461010090046001600160a01b0316631c75e369338360006040519080825280601f01601f191660200182016040528015611da2576020820181803683370190505b506040518463ffffffff1660e01b815260040180846001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611e04578181015183820152602001611dec565b50505050905090810190601f168015611e315780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015611e5257600080fd5b505af1158015611e66573d6000803e3d6000fd5b505050505b33600090815260166020908152604091829020825160a081018452858152918201889052918101611e9e87611c09613760565b815260075461010090046001600160a01b0390811660208084019190915260409283019590955283546001808201865560009586529486902084516005909202019081559483015193850193909355810151600284015560608101516003840180546001600160a01b031916919093161790915560800151600490910155611503565b611f29613760565b336000818152601760205260409020919091556115039085836137cb565b611f4f612e48565b6001600160a01b0316611f606126b3565b6001600160a01b031614611fbb576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b87891115611ffa5760405162461bcd60e51b815260040180806020018281038252602881526020018061409e6028913960400191505060405180910390fd5b8587106120385760405162461bcd60e51b815260040180806020018281038252602b815260200180614139602b913960400191505060405180910390fd5b6064881115801561204a575060648511155b6120855760405162461bcd60e51b815260040180806020018281038252602881526020018061409e6028913960400191505060405180910390fd5b841561229c5760075461010090046001600160a01b03166120d75760405162461bcd60e51b81526004018080602001828103825260368152602001806140c66036913960400191505060405180910390fd5b6007546040805160008082526020820192839052631c75e36960e01b83523060248301818152604484018390526060606485019081528451608486018190526101009097046001600160a01b031696631c75e3699693959360a4850191808383895b83811015612151578181015183820152602001612139565b50505050905090810190601f16801561217e5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561219f57600080fd5b505af11580156121b3573d6000803e3d6000fd5b5050600754604080516000808252602082019283905263549230c960e01b83523060248301818152604484018390526060606485019081528451608486018190526101009097046001600160a01b0316985063549230c9975091959294909260a485019190808383895b8381101561223557818101518382015260200161221d565b50505050905090810190601f1680156122625780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561228357600080fd5b505af1158015612297573d6000803e3d6000fd5b505050505b6127108411156122dd5760405162461bcd60e51b815260040180806020018281038252602f81526020018061423e602f913960400191505060405180910390fd5b600c899055600d889055600e879055600f86905560108590556011849055601283905560138290556014819055604080518a8152602081018a9052808201899052606081018890526080810187905260a0810186905260c0810185905260e08101849052610100810183905290517fb9c8ffc7292d7c858045085b93d56517f73f58b221e8312ef0020bc86a4a2c82918190036101200190a1505050505050505050565b60008161238f575081610f42565b600e548210156123a157506000610f42565b600f548211156123cc576123c56064611d50600d548661370790919063ffffffff16565b9050610f42565b60006124226124196123eb600e54600f5461336e90919063ffffffff16565b611d50612405600c54600d5461336e90919063ffffffff16565b600e5461241390899061336e565b90613707565b600c5490613308565b90506124336064611d508684613707565b949350505050565b612443612e48565b6001600160a01b03166124546126b3565b6001600160a01b0316146124af576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661250a576040805162461bcd60e51b815260206004820152601860248201527f496e76616c696420747265617375727920616464726573730000000000000000604482015290519081900360640190fd5b6018546040516001600160a01b03918216918316907f5634a90413b79beba6c5f37aa8f19d1aee84a5320ff20ac7bd1ac63280867d5c90600090a3601880546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0316600090815260156020526040902080546001909101549091565b612592612e48565b6001600160a01b03166125a36126b3565b6001600160a01b0316146125fe576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0382163014156126465760405162461bcd60e51b815260040180806020018281038252603d8152602001806140fc603d913960400191505060405180910390fd5b801561265d57612657600883612e33565b5061266a565b6126686008836138a0565b505b604080516001600160a01b0384168152821515602082015281517f3a34209cb941a5d23a56dea730a13738454bc7daefd4bb32e8d7df58c1bd920d929181900390910190a15050565b6000546001600160a01b031690565b60068054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610f205780601f10610ef557610100808354040283529160200191610f20565b60026001541415612769576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556115c08233836134db565b60115481565b600260015414156127c5576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556127d4813361362f565b5060018055565b6000610f3e6127e8612e48565b846110ed856040518060600160405280602581526020016143a66025913960036000612812612e48565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190613271565b6000610f3e612850612e48565b8484613114565b6002600154141561289d576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015533600081815260166020526040902054829081106128f15760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b3360009081526015602090815260408083206016909252822080549192918690811061291957fe5b906000526020600020906005020190508060020154612936613760565b10156129735760405162461bcd60e51b815260040180806020018281038252603281526020018061434a6032913960400191505060405180910390fd5b601454816002015401612984613760565b11156129c15760405162461bcd60e51b815260040180806020018281038252602481526020018061426d6024913960400191505060405180910390fd5b600180820154908301546129d49161336e565b82600101819055506129ef33826001015483600001546137cb565b600481015415612ae75760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b83811015612a80578181015183820152602001612a68565b50505050905090810190601f168015612aad5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015612ace57600080fd5b505af1158015612ae2573d6000803e3d6000fd5b505050505b6118a8856133cb565b6001600160a01b031660009081526016602052604090205490565b60026001541415612b51576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556001600160a01b038216612b9b5760405162461bcd60e51b81526004018080602001828103825260298152602001806141ab6029913960400191505060405180910390fd5b336000818152600a602090815260408083206001600160a01b03871680855290835292819020859055805185815290519293927fe75ec259c38e4601f24580968665ec00b21cca4f996689b260ec598aec5c08db929181900390910190a3505060018055565b600e5481565b6018546001600160a01b031681565b6001600160a01b0382166000908152601660205260408120548190819081908190879087908110612c785760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b6001600160a01b038916600090815260166020526040812080548a908110612c9c57fe5b600091825260209091206005909102018054600182015460028301546003840154600490940154929e919d509b506001600160a01b039092169950975095505050505050565b60145481565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b600d5481565b600f5481565b612d27612e48565b6001600160a01b0316612d386126b3565b6001600160a01b031614612d93576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116612dd85760405162461bcd60e51b81526004018080602001828103825260268152602001806140566026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006110f6836001600160a01b0384166138b5565b3390565b6001600160a01b038316612e915760405162461bcd60e51b81526004018080602001828103825260248152602001806142d76024913960400191505060405180910390fd5b6001600160a01b038216612ed65760405162461bcd60e51b815260040180806020018281038252602281526020018061407c6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260036020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6000610f42826138ff565b60008111612f98576040805162461bcd60e51b815260206004820152601f60248201527f616c6c6f636174653a20616d6f756e742063616e6e6f74206265206e756c6c00604482015290519081900360640190fd5b6001600160a01b038084166000908152601560209081526040808320600a83528184209487168452939091529020548281101561301c576040805162461bcd60e51b815260206004820152601f60248201527f616c6c6f636174653a206e6f6e20617574686f72697a656420616d6f756e7400604482015290519081900360640190fd5b613026818461336e565b6001600160a01b038087166000818152600a60209081526040808320948a1680845294825280832095909555918152600b8252838120928152919052205461306e9084613308565b6001600160a01b038087166000908152600b602090815260408083209389168352929052205581546130a09084613308565b82556130ad853085613114565b836001600160a01b0316856001600160a01b03167f5168bfb88d6125d4580e2b91ecb103a730312c3e8b0be9c4031a0fc794e2cd5f856040518082815260200191505060405180910390a35050505050565b60006110f6836001600160a01b038416613903565b6001600160a01b0383166131595760405162461bcd60e51b81526004018080602001828103825260258152602001806142b26025913960400191505060405180910390fd5b6001600160a01b03821661319e5760405162461bcd60e51b8152600401808060200182810382526023815260200180613ff16023913960400191505060405180910390fd5b6131a983838361391b565b6131e681604051806060016040528060268152602001614164602691396001600160a01b0386166000908152600260205260409020549190613271565b6001600160a01b0380851660009081526002602052604080822093909355908416815220546132159082613308565b6001600160a01b0380841660008181526002602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156133005760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c55781810151838201526020016132ad565b50505050905090810190601f1680156132f25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828201838110156110f6576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006110f6838361399e565b6000828211156133c5576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b336000908152601660205260409020805460001981019081106133ea57fe5b906000526020600020906005020160166000336001600160a01b03166001600160a01b03168152602001908152602001600020828154811061342857fe5b600091825260208083208454600590930201918255600180850154908301556002808501549083015560038085015490830180546001600160a01b0319166001600160a01b03909216919091179055600493840154939091019290925533815260169091526040902080548061349a57fe5b60008281526020812060056000199093019283020181815560018101829055600281018290556003810180546001600160a01b031916905560040155905550565b6000811161351a5760405162461bcd60e51b81526004018080602001828103825260218152602001806141d46021913960400191505060405180910390fd5b6001600160a01b038084166000908152600b60209081526040808320938616835292905220548181101561357f5760405162461bcd60e51b815260040180806020018281038252602181526020018061418a6021913960400191505060405180910390fd5b613589818361336e565b6001600160a01b038086166000818152600b602090815260408083209489168352938152838220949094559081526015909252902080546135ca908461336e565b81556135d7308685613114565b836001600160a01b0316856001600160a01b03167f404a64c4c9e321c386d785aab5c00b4cf3279807f20c76dcf5356399bcc1c338856040518082815260200191505060405180910390a35050505050565b3b151590565b81613681576040805162461bcd60e51b815260206004820152601e60248201527f636f6e766572743a20616d6f756e742063616e6e6f74206265206e756c6c0000604482015290519081900360640190fd5b61368b8183613a02565b604080516001600160a01b038316815260208101849052815133927fccfaeb3043a96a967dc036ab72e078a9632af809671bc2a1ac30a8043645f89e928290030190a26137036001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333085613af4565b5050565b60008261371657506000610f42565b8282028284828161372357fe5b04146110f65760405162461bcd60e51b81526004018080602001828103825260218152602001806141f56021913960400191505060405180910390fd5b4290565b60008082116137ba576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b8183816137c357fe5b049392505050565b60006137d7838361336e565b905061380d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168584613b69565b801561384d5760185461384d906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683613b69565b6138573084613bd0565b604080518481526020810184905281516001600160a01b038716927f0da072ebd7a5649099f43a3776eb0cda17aca79426ee9f28aae203f5dfa04eda928290030190a250505050565b60006110f6836001600160a01b038416613ccc565b60006138c18383613903565b6138f757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f42565b506000610f42565b5490565b60009081526001919091016020526040902054151590565b6001600160a01b038316158061393757506139376008846130ff565b8061394857506139486008836130ff565b613999576040805162461bcd60e51b815260206004820152601560248201527f7472616e736665723a206e6f7420616c6c6f7765640000000000000000000000604482015290519081900360640190fd5b505050565b815460009082106139e05760405162461bcd60e51b8152600401808060200182810382526022815260200180613fcf6022913960400191505060405180910390fd5b8260000182815481106139ef57fe5b9060005260206000200154905092915050565b6001600160a01b038216613a5d576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b613a696000838361391b565b600454613a769082613308565b6004556001600160a01b038216600090815260026020526040902054613a9c9082613308565b6001600160a01b03831660008181526002602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b179052613b63908590613d92565b50505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b179052613999908490613d92565b6001600160a01b038216613c155760405162461bcd60e51b81526004018080602001828103825260218152602001806142916021913960400191505060405180910390fd5b613c218260008361391b565b613c5e81604051806060016040528060228152602001614034602291396001600160a01b0385166000908152600260205260409020549190613271565b6001600160a01b038316600090815260026020526040902055600454613c84908261336e565b6004556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b60008181526001830160205260408120548015613d885783546000198083019190810190600090879083908110613cff57fe5b9060005260206000200154905080876000018481548110613d1c57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080613d4c57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610f42565b6000915050610f42565b6000613de7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613e439092919063ffffffff16565b80519091501561399957808060200190516020811015613e0657600080fd5b50516139995760405162461bcd60e51b815260040180806020018281038252602a81526020018061437c602a913960400191505060405180910390fd5b6060612433848460008585613e5785613629565b613ea8576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310613ee65780518252601f199092019160209182019101613ec7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613f48576040519150601f19603f3d011682016040523d82523d6000602084013e613f4d565b606091505b5091509150613f5d828286613f68565b979650505050505050565b60608315613f775750816110f6565b825115613f875782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156132c55781810151838201526020016132ad56fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e647345524332303a207472616e7366657220746f20746865207a65726f20616464726573735265656e7472616e637947756172643a207265656e7472616e742063616c6c0045524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737375706461746552656465656d53657474696e67733a2077726f6e6720726174696f2076616c75657375706461746552656465656d53657474696e67733a206469766964656e647341646472657373206e6f7420696e697469616c697a65647570646174655472616e7366657257686974656c6973743a2043616e6e6f742072656d6f7665206573546f6b656e2066726f6d2077686974656c69737475706461746552656465656d53657474696e67733a2077726f6e67206475726174696f6e2076616c75657345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63656465616c6c6f636174653a206e6f6e20617574686f72697a656420616d6f756e74617070726f766555736167653a20617070726f766520746f20746865207a65726f20616464726573736465616c6c6f636174653a20616d6f756e742063616e6e6f74206265206e756c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636575706461746552656465656d53657474696e67733a20696e76616c69642072656465656d2070657263656e7461676566696e616c697a6552656465656d3a20677261636520706572696f64206578706972656445524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737376616c696461746552656465656d3a2072656465656d20656e74727920646f6573206e6f7420657869737472656465656d3a206573546f6b656e416d6f756e742063616e6e6f74206265206e756c6c66696e616c697a6552656465656d3a2076657374696e67206475726174696f6e20686173206e6f7420656e646564207965745361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa164736f6c6343000706000a0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c4000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004ab9070b7680f802cbf8322e597a4409902171e5000000000000000000000000000000000000000000000000000000000000000c457363726f77656420414c42000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056573414c42000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103785760003560e01c8063619ac95b116101d3578063a3908e1b11610104578063c5f956af116100a2578063dd62ed3e1161007c578063dd62ed3e14610b5d578063e3a2950b14610b8b578063e9ed87f814610b93578063f2fde38b14610b9b57610378565b8063c5f956af14610b21578063cc6c542314610b29578063d402bfd314610b5557610378565b8063aff6cbf1116100de578063aff6cbf114610aaa578063b90c2b5214610ac7578063c360ed1c14610aed578063c4b1076614610b1957610378565b8063a3908e1b14610a35578063a457c2d714610a52578063a9059cbb14610a7e57610378565b8063841e4561116101715780638da5cb5b1161014b5780638da5cb5b146109f157806395d89b41146109f9578063a0bdc7cb14610a01578063a303203914610a2d57610378565b8063841e45611461097757806388adb48d1461099d57806389083654146109c357610378565b80637afff699116101ad5780637afff699146108bc5780637cbc2373146108e25780637f10c94b14610905578063806af0b41461095457610378565b8063619ac95b1461088657806370a082311461088e578063715018a6146108b457610378565b8063313ce567116102ad5780634a5b406e1161024b578063539ffb7711610225578063539ffb7714610779578063549230c9146107965780635a1d34dc1461081b5780635cdd175f1461084757610378565b80634a5b406e146106f45780634b359d38146106fc5780634f62b7ec1461071957610378565b80633b90f9a0116102875780633b90f9a01461068a5780633c0546b7146106b6578063488c8303146106be578063497965ee146106ec57610378565b8063313ce567146106385780633862e21a14610656578063395093511461065e57610378565b80631eee7e601161031a5780632b489679116102f45780632b489679146105915780632cc2f5ce146105bf5780632e9a76e4146105ed57806331124ce31461061b57610378565b80631eee7e601461051157806323b872dd146105375780632495a5991461056d57610378565b8063161aab4311610356578063161aab431461046257806318160ddd1461047c5780631c352679146104845780631c75e3691461048c57610378565b806306045a211461037d57806306fdde03146103a5578063095ea7b314610422575b600080fd5b6103a36004803603602081101561039357600080fd5b50356001600160a01b0316610bc1565b005b6103ad610e94565b6040805160208082528351818301528351919283929083019185019080838360005b838110156103e75781810151838201526020016103cf565b50505050905090810190601f1680156104145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61044e6004803603604081101561043857600080fd5b506001600160a01b038135169060200135610f2a565b604080519115158252519081900360200190f35b61046a610f48565b60408051918252519081900360200190f35b61046a610f59565b61046a610f5f565b6103a3600480360360608110156104a257600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156104d257600080fd5b8201836020820111156104e457600080fd5b8035906020019184600183028401116401000000008311171561050657600080fd5b509092509050610f65565b61044e6004803603602081101561052757600080fd5b50356001600160a01b0316611068565b61044e6004803603606081101561054d57600080fd5b506001600160a01b03813581169160208101359091169060400135611075565b6105756110fd565b604080516001600160a01b039092168252519081900360200190f35b61046a600480360360408110156105a757600080fd5b506001600160a01b0381358116916020013516611121565b61046a600480360360408110156105d557600080fd5b506001600160a01b038135811691602001351661114c565b61046a6004803603604081101561060357600080fd5b506001600160a01b0381358116916020013516611169565b6103a36004803603602081101561063157600080fd5b5035611194565b61064061150d565b6040805160ff9092168252519081900360200190f35b61046a611516565b61044e6004803603604081101561067457600080fd5b506001600160a01b03813516906020013561151c565b6103a3600480360360408110156106a057600080fd5b506001600160a01b03813516906020013561156a565b61046a6115c8565b61046a600480360360408110156106d457600080fd5b506001600160a01b03813581169160200135166115ce565b6105756115eb565b61046a6115ff565b6105756004803603602081101561071257600080fd5b5035611605565b6107456004803603604081101561072f57600080fd5b506001600160a01b038135169060200135611612565b604080519586526020860194909452848401929092526001600160a01b031660608401526080830152519081900360a00190f35b6103a36004803603602081101561078f57600080fd5b5035611669565b6103a3600480360360608110156107ac57600080fd5b6001600160a01b03823516916020810135918101906060810160408201356401000000008111156107dc57600080fd5b8201836020820111156107ee57600080fd5b8035906020019184600183028401116401000000008311171561081057600080fd5b5090925090506118b3565b6103a36004803603604081101561083157600080fd5b50803590602001356001600160a01b0316611994565b61086d6004803603602081101561085d57600080fd5b50356001600160a01b0316611a43565b6040805192835260208301919091528051918290030190f35b61046a611a5c565b61046a600480360360208110156108a457600080fd5b50356001600160a01b0316611a61565b6103a3611a7c565b61046a600480360360208110156108d257600080fd5b50356001600160a01b0316611b3a565b6103a3600480360360408110156108f857600080fd5b5080359060200135611b4c565b6103a3600480360361012081101561091c57600080fd5b5080359060208101359060408101359060608101359060808101359060a08101359060c08101359060e0810135906101000135611f47565b61046a6004803603604081101561096a57600080fd5b5080359060200135612381565b6103a36004803603602081101561098d57600080fd5b50356001600160a01b031661243b565b61086d600480360360208110156109b357600080fd5b50356001600160a01b0316612567565b6103a3600480360360408110156109d957600080fd5b506001600160a01b038135169060200135151561258a565b6105756126b3565b6103ad6126c2565b6103a360048036036040811015610a1757600080fd5b506001600160a01b038135169060200135612723565b61046a612779565b6103a360048036036020811015610a4b57600080fd5b503561277f565b61044e60048036036040811015610a6857600080fd5b506001600160a01b0381351690602001356127db565b61044e60048036036040811015610a9457600080fd5b506001600160a01b038135169060200135612843565b6103a360048036036020811015610ac057600080fd5b5035612857565b61046a60048036036020811015610add57600080fd5b50356001600160a01b0316612af0565b6103a360048036036040811015610b0357600080fd5b506001600160a01b038135169060200135612b0b565b61046a612c01565b610575612c07565b61074560048036036040811015610b3f57600080fd5b506001600160a01b038135169060200135612c16565b61046a612ce2565b61046a60048036036040811015610b7357600080fd5b506001600160a01b0381358116916020013516612ce8565b61046a612d13565b61046a612d19565b6103a360048036036020811015610bb157600080fd5b50356001600160a01b0316612d1f565b610bc9612e48565b6001600160a01b0316610bda6126b3565b6001600160a01b031614610c35576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610c4d576000601055610e08565b6040805160008082526020820192839052631c75e36960e01b83523060248301818152604484018390526060606485019081528451608486018190526001600160a01b03881696631c75e3699694959493929160a485019190808383895b83811015610cc3578181015183820152602001610cab565b50505050905090810190601f168015610cf05780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015610d1157600080fd5b505af1158015610d25573d6000803e3d6000fd5b5050604080516000808252602082019283905263549230c960e01b83523060248301818152604484018390526060606485019081528451608486018190526001600160a01b038a16985063549230c9975092959394939192909160a4850191808383895b83811015610da1578181015183820152602001610d89565b50505050905090810190601f168015610dce5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015610def57600080fd5b505af1158015610e03573d6000803e3d6000fd5b505050505b600754604080516001600160a01b0361010090930483168152918316602083015280517f044c75b8fa43ce72364b4c23fdb8451beafbda46505bf44c76f0853a01ed4ade9281900390910190a1600780546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610f205780601f10610ef557610100808354040283529160200191610f20565b820191906000526020600020905b815481529060010190602001808311610f0357829003601f168201915b5050505050905090565b6000610f3e610f37612e48565b8484612e4c565b5060015b92915050565b6000610f546008612f38565b905090565b60045490565b600c5481565b60026001541415610fab576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b6002600155610fbb338585612f43565b836001600160a01b0316631c75e369338585856040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505060018055505050505050565b6000610f426008836130ff565b6000611082848484613114565b6110f28461108e612e48565b6110ed85604051806060016040528060288152602001614216602891396001600160a01b038a166000908152600360205260408120906110cc612e48565b6001600160a01b031681526020810191909152604001600020549190613271565b612e4c565b5060015b9392505050565b7f0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c481565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205490565b600b60209081526000928352604080842090915290825290205481565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205490565b600260015414156111da576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b6002600155336000818152601660205260409020548290811061122e5760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b33600090815260166020526040812080548590811061124957fe5b6000918252602090912060059091020160038101546007549192506001600160a01b039081166101009092041614801590611293575060075461010090046001600160a01b031615155b156115035760048101541561147d5760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b83811015611329578181015183820152602001611311565b50505050905090810190601f1680156113565780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561137757600080fd5b505af115801561138b573d6000803e3d6000fd5b505060075460048401546040805160008082526020820192839052631c75e36960e01b83523360248301818152604484018690526060606485019081528451608486018190526101009098046001600160a01b03169950631c75e3699850919693949093919260a4860192908190849084905b838110156114165781810151838201526020016113fe565b50505050905090810190601f1680156114435780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561146457600080fd5b505af1158015611478573d6000803e3d6000fd5b505050505b6003810154600754604080518781526001600160a01b03938416602082015261010090920490921681830152905133917fa60c8f9118be22c9277a8129333d64ffda3de44ca7a5831d077a3127f1237a18919081900360600190a26007546003820180546101009092046001600160a01b03166001600160a01b03199092169190911790555b5050600180555050565b60075460ff1690565b60125481565b6000610f3e611529612e48565b846110ed856003600061153a612e48565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490613308565b600260015414156115b0576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556115c0823383612f43565b505060018055565b60135481565b600a60209081526000928352604080842090915290825290205481565b60075461010090046001600160a01b031681565b60105481565b6000610f42600883613362565b6016602052816000526040600020818154811061162e57600080fd5b600091825260209091206005909102018054600182015460028301546003840154600490940154929550909350916001600160a01b03169085565b600260015414156116af576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015533600081815260166020526040902054829081106117035760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b3360009081526015602090815260408083206016909252822080549192918690811061172b57fe5b906000526020600020906005020190506117568160010154836001015461336e90919063ffffffff16565b826001018190555061176d30338360010154613114565b6004810154156118655760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b838110156117fe5781810151838201526020016117e6565b50505050905090810190601f16801561182b5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561184c57600080fd5b505af1158015611860573d6000803e3d6000fd5b505050505b6001810154604080519182525133917f56d7520e387607a8daa892e3fed116badc2a636307bdc794b1c1aed97ae203f4919081900360200190a26118a8856133cb565b505060018055505050565b600260015414156118f9576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556119093385856134db565b836001600160a01b031663549230c9338585856040518563ffffffff1660e01b815260040180856001600160a01b03168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b15801561104657600080fd5b600260015414156119da576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556119e833613629565b611a39576040805162461bcd60e51b815260206004820152601660248201527f636f6e76657274546f3a206e6f7420616c6c6f77656400000000000000000000604482015290519081900360640190fd5b6115c0828261362f565b6015602052600090815260409020805460019091015482565b606481565b6001600160a01b031660009081526002602052604090205490565b611a84612e48565b6001600160a01b0316611a956126b3565b6001600160a01b031614611af0576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60176020526000908152604090205481565b60026001541415611b92576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015581611bd35760405162461bcd60e51b81526004018080602001828103825260248152602001806143266024913960400191505060405180910390fd5b3360008181526015602090815260408083206017909252822054600182015482549294919392611c0f92611c0991908290611a61565b90613308565b90506000611c3f601254612710611c316011548661370790919063ffffffff16565b81611c3857fe5b0490613308565b905084151580611c5957506013548301611c57613760565b105b80611c6357508086115b15611cbf57600e54851015611cbf576040805162461bcd60e51b815260206004820152601760248201527f72656465656d3a20696e76616c69642072657175657374000000000000000000604482015290519081900360640190fd5b505050611ccd333085613114565b6000611cd98484612381565b6040805186815260208101839052808201869052905191925033917fbd5034ffbd47e4e72a94baa2cdb74c6fad73cb3bcdc13036b72ec8306f5a76469181900360600190a28215611f21576001820154611d339085613308565b6001830155601054600090611d5690606490611d50908890613707565b90613764565b90508015611e6b5760075461010090046001600160a01b0316631c75e369338360006040519080825280601f01601f191660200182016040528015611da2576020820181803683370190505b506040518463ffffffff1660e01b815260040180846001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611e04578181015183820152602001611dec565b50505050905090810190601f168015611e315780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015611e5257600080fd5b505af1158015611e66573d6000803e3d6000fd5b505050505b33600090815260166020908152604091829020825160a081018452858152918201889052918101611e9e87611c09613760565b815260075461010090046001600160a01b0390811660208084019190915260409283019590955283546001808201865560009586529486902084516005909202019081559483015193850193909355810151600284015560608101516003840180546001600160a01b031916919093161790915560800151600490910155611503565b611f29613760565b336000818152601760205260409020919091556115039085836137cb565b611f4f612e48565b6001600160a01b0316611f606126b3565b6001600160a01b031614611fbb576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b87891115611ffa5760405162461bcd60e51b815260040180806020018281038252602881526020018061409e6028913960400191505060405180910390fd5b8587106120385760405162461bcd60e51b815260040180806020018281038252602b815260200180614139602b913960400191505060405180910390fd5b6064881115801561204a575060648511155b6120855760405162461bcd60e51b815260040180806020018281038252602881526020018061409e6028913960400191505060405180910390fd5b841561229c5760075461010090046001600160a01b03166120d75760405162461bcd60e51b81526004018080602001828103825260368152602001806140c66036913960400191505060405180910390fd5b6007546040805160008082526020820192839052631c75e36960e01b83523060248301818152604484018390526060606485019081528451608486018190526101009097046001600160a01b031696631c75e3699693959360a4850191808383895b83811015612151578181015183820152602001612139565b50505050905090810190601f16801561217e5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561219f57600080fd5b505af11580156121b3573d6000803e3d6000fd5b5050600754604080516000808252602082019283905263549230c960e01b83523060248301818152604484018390526060606485019081528451608486018190526101009097046001600160a01b0316985063549230c9975091959294909260a485019190808383895b8381101561223557818101518382015260200161221d565b50505050905090810190601f1680156122625780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b15801561228357600080fd5b505af1158015612297573d6000803e3d6000fd5b505050505b6127108411156122dd5760405162461bcd60e51b815260040180806020018281038252602f81526020018061423e602f913960400191505060405180910390fd5b600c899055600d889055600e879055600f86905560108590556011849055601283905560138290556014819055604080518a8152602081018a9052808201899052606081018890526080810187905260a0810186905260c0810185905260e08101849052610100810183905290517fb9c8ffc7292d7c858045085b93d56517f73f58b221e8312ef0020bc86a4a2c82918190036101200190a1505050505050505050565b60008161238f575081610f42565b600e548210156123a157506000610f42565b600f548211156123cc576123c56064611d50600d548661370790919063ffffffff16565b9050610f42565b60006124226124196123eb600e54600f5461336e90919063ffffffff16565b611d50612405600c54600d5461336e90919063ffffffff16565b600e5461241390899061336e565b90613707565b600c5490613308565b90506124336064611d508684613707565b949350505050565b612443612e48565b6001600160a01b03166124546126b3565b6001600160a01b0316146124af576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b03811661250a576040805162461bcd60e51b815260206004820152601860248201527f496e76616c696420747265617375727920616464726573730000000000000000604482015290519081900360640190fd5b6018546040516001600160a01b03918216918316907f5634a90413b79beba6c5f37aa8f19d1aee84a5320ff20ac7bd1ac63280867d5c90600090a3601880546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0316600090815260156020526040902080546001909101549091565b612592612e48565b6001600160a01b03166125a36126b3565b6001600160a01b0316146125fe576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0382163014156126465760405162461bcd60e51b815260040180806020018281038252603d8152602001806140fc603d913960400191505060405180910390fd5b801561265d57612657600883612e33565b5061266a565b6126686008836138a0565b505b604080516001600160a01b0384168152821515602082015281517f3a34209cb941a5d23a56dea730a13738454bc7daefd4bb32e8d7df58c1bd920d929181900390910190a15050565b6000546001600160a01b031690565b60068054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610f205780601f10610ef557610100808354040283529160200191610f20565b60026001541415612769576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556115c08233836134db565b60115481565b600260015414156127c5576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556127d4813361362f565b5060018055565b6000610f3e6127e8612e48565b846110ed856040518060600160405280602581526020016143a66025913960036000612812612e48565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190613271565b6000610f3e612850612e48565b8484613114565b6002600154141561289d576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b600260015533600081815260166020526040902054829081106128f15760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b3360009081526015602090815260408083206016909252822080549192918690811061291957fe5b906000526020600020906005020190508060020154612936613760565b10156129735760405162461bcd60e51b815260040180806020018281038252603281526020018061434a6032913960400191505060405180910390fd5b601454816002015401612984613760565b11156129c15760405162461bcd60e51b815260040180806020018281038252602481526020018061426d6024913960400191505060405180910390fd5b600180820154908301546129d49161336e565b82600101819055506129ef33826001015483600001546137cb565b600481015415612ae75760038101546004820154604080516000808252602082019283905263549230c960e01b83523360248301818152604484018690526060606485019081528451608486018190526001600160a01b039098169763549230c99793969395949293919260a486019291908190849084905b83811015612a80578181015183820152602001612a68565b50505050905090810190601f168015612aad5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b158015612ace57600080fd5b505af1158015612ae2573d6000803e3d6000fd5b505050505b6118a8856133cb565b6001600160a01b031660009081526016602052604090205490565b60026001541415612b51576040805162461bcd60e51b815260206004820152601f6024820152600080516020614014833981519152604482015290519081900360640190fd5b60026001556001600160a01b038216612b9b5760405162461bcd60e51b81526004018080602001828103825260298152602001806141ab6029913960400191505060405180910390fd5b336000818152600a602090815260408083206001600160a01b03871680855290835292819020859055805185815290519293927fe75ec259c38e4601f24580968665ec00b21cca4f996689b260ec598aec5c08db929181900390910190a3505060018055565b600e5481565b6018546001600160a01b031681565b6001600160a01b0382166000908152601660205260408120548190819081908190879087908110612c785760405162461bcd60e51b815260040180806020018281038252602b8152602001806142fb602b913960400191505060405180910390fd5b6001600160a01b038916600090815260166020526040812080548a908110612c9c57fe5b600091825260209091206005909102018054600182015460028301546003840154600490940154929e919d509b506001600160a01b039092169950975095505050505050565b60145481565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b600d5481565b600f5481565b612d27612e48565b6001600160a01b0316612d386126b3565b6001600160a01b031614612d93576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116612dd85760405162461bcd60e51b81526004018080602001828103825260268152602001806140566026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006110f6836001600160a01b0384166138b5565b3390565b6001600160a01b038316612e915760405162461bcd60e51b81526004018080602001828103825260248152602001806142d76024913960400191505060405180910390fd5b6001600160a01b038216612ed65760405162461bcd60e51b815260040180806020018281038252602281526020018061407c6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260036020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6000610f42826138ff565b60008111612f98576040805162461bcd60e51b815260206004820152601f60248201527f616c6c6f636174653a20616d6f756e742063616e6e6f74206265206e756c6c00604482015290519081900360640190fd5b6001600160a01b038084166000908152601560209081526040808320600a83528184209487168452939091529020548281101561301c576040805162461bcd60e51b815260206004820152601f60248201527f616c6c6f636174653a206e6f6e20617574686f72697a656420616d6f756e7400604482015290519081900360640190fd5b613026818461336e565b6001600160a01b038087166000818152600a60209081526040808320948a1680845294825280832095909555918152600b8252838120928152919052205461306e9084613308565b6001600160a01b038087166000908152600b602090815260408083209389168352929052205581546130a09084613308565b82556130ad853085613114565b836001600160a01b0316856001600160a01b03167f5168bfb88d6125d4580e2b91ecb103a730312c3e8b0be9c4031a0fc794e2cd5f856040518082815260200191505060405180910390a35050505050565b60006110f6836001600160a01b038416613903565b6001600160a01b0383166131595760405162461bcd60e51b81526004018080602001828103825260258152602001806142b26025913960400191505060405180910390fd5b6001600160a01b03821661319e5760405162461bcd60e51b8152600401808060200182810382526023815260200180613ff16023913960400191505060405180910390fd5b6131a983838361391b565b6131e681604051806060016040528060268152602001614164602691396001600160a01b0386166000908152600260205260409020549190613271565b6001600160a01b0380851660009081526002602052604080822093909355908416815220546132159082613308565b6001600160a01b0380841660008181526002602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156133005760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c55781810151838201526020016132ad565b50505050905090810190601f1680156132f25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000828201838110156110f6576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006110f6838361399e565b6000828211156133c5576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b336000908152601660205260409020805460001981019081106133ea57fe5b906000526020600020906005020160166000336001600160a01b03166001600160a01b03168152602001908152602001600020828154811061342857fe5b600091825260208083208454600590930201918255600180850154908301556002808501549083015560038085015490830180546001600160a01b0319166001600160a01b03909216919091179055600493840154939091019290925533815260169091526040902080548061349a57fe5b60008281526020812060056000199093019283020181815560018101829055600281018290556003810180546001600160a01b031916905560040155905550565b6000811161351a5760405162461bcd60e51b81526004018080602001828103825260218152602001806141d46021913960400191505060405180910390fd5b6001600160a01b038084166000908152600b60209081526040808320938616835292905220548181101561357f5760405162461bcd60e51b815260040180806020018281038252602181526020018061418a6021913960400191505060405180910390fd5b613589818361336e565b6001600160a01b038086166000818152600b602090815260408083209489168352938152838220949094559081526015909252902080546135ca908461336e565b81556135d7308685613114565b836001600160a01b0316856001600160a01b03167f404a64c4c9e321c386d785aab5c00b4cf3279807f20c76dcf5356399bcc1c338856040518082815260200191505060405180910390a35050505050565b3b151590565b81613681576040805162461bcd60e51b815260206004820152601e60248201527f636f6e766572743a20616d6f756e742063616e6e6f74206265206e756c6c0000604482015290519081900360640190fd5b61368b8183613a02565b604080516001600160a01b038316815260208101849052815133927fccfaeb3043a96a967dc036ab72e078a9632af809671bc2a1ac30a8043645f89e928290030190a26137036001600160a01b037f0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c416333085613af4565b5050565b60008261371657506000610f42565b8282028284828161372357fe5b04146110f65760405162461bcd60e51b81526004018080602001828103825260218152602001806141f56021913960400191505060405180910390fd5b4290565b60008082116137ba576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b8183816137c357fe5b049392505050565b60006137d7838361336e565b905061380d6001600160a01b037f0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c4168584613b69565b801561384d5760185461384d906001600160a01b037f0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c48116911683613b69565b6138573084613bd0565b604080518481526020810184905281516001600160a01b038716927f0da072ebd7a5649099f43a3776eb0cda17aca79426ee9f28aae203f5dfa04eda928290030190a250505050565b60006110f6836001600160a01b038416613ccc565b60006138c18383613903565b6138f757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610f42565b506000610f42565b5490565b60009081526001919091016020526040902054151590565b6001600160a01b038316158061393757506139376008846130ff565b8061394857506139486008836130ff565b613999576040805162461bcd60e51b815260206004820152601560248201527f7472616e736665723a206e6f7420616c6c6f7765640000000000000000000000604482015290519081900360640190fd5b505050565b815460009082106139e05760405162461bcd60e51b8152600401808060200182810382526022815260200180613fcf6022913960400191505060405180910390fd5b8260000182815481106139ef57fe5b9060005260206000200154905092915050565b6001600160a01b038216613a5d576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b613a696000838361391b565b600454613a769082613308565b6004556001600160a01b038216600090815260026020526040902054613a9c9082613308565b6001600160a01b03831660008181526002602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b179052613b63908590613d92565b50505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b179052613999908490613d92565b6001600160a01b038216613c155760405162461bcd60e51b81526004018080602001828103825260218152602001806142916021913960400191505060405180910390fd5b613c218260008361391b565b613c5e81604051806060016040528060228152602001614034602291396001600160a01b0385166000908152600260205260409020549190613271565b6001600160a01b038316600090815260026020526040902055600454613c84908261336e565b6004556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b60008181526001830160205260408120548015613d885783546000198083019190810190600090879083908110613cff57fe5b9060005260206000200154905080876000018481548110613d1c57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080613d4c57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610f42565b6000915050610f42565b6000613de7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613e439092919063ffffffff16565b80519091501561399957808060200190516020811015613e0657600080fd5b50516139995760405162461bcd60e51b815260040180806020018281038252602a81526020018061437c602a913960400191505060405180910390fd5b6060612433848460008585613e5785613629565b613ea8576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310613ee65780518252601f199092019160209182019101613ec7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613f48576040519150601f19603f3d011682016040523d82523d6000602084013e613f4d565b606091505b5091509150613f5d828286613f68565b979650505050505050565b60608315613f775750816110f6565b825115613f875782518084602001fd5b60405162461bcd60e51b81526020600482018181528451602484015284518593919283926044019190850190808383600083156132c55781810151838201526020016132ad56fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e647345524332303a207472616e7366657220746f20746865207a65726f20616464726573735265656e7472616e637947756172643a207265656e7472616e742063616c6c0045524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737375706461746552656465656d53657474696e67733a2077726f6e6720726174696f2076616c75657375706461746552656465656d53657474696e67733a206469766964656e647341646472657373206e6f7420696e697469616c697a65647570646174655472616e7366657257686974656c6973743a2043616e6e6f742072656d6f7665206573546f6b656e2066726f6d2077686974656c69737475706461746552656465656d53657474696e67733a2077726f6e67206475726174696f6e2076616c75657345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63656465616c6c6f636174653a206e6f6e20617574686f72697a656420616d6f756e74617070726f766555736167653a20617070726f766520746f20746865207a65726f20616464726573736465616c6c6f636174653a20616d6f756e742063616e6e6f74206265206e756c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636575706461746552656465656d53657474696e67733a20696e76616c69642072656465656d2070657263656e7461676566696e616c697a6552656465656d3a20677261636520706572696f64206578706972656445524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737376616c696461746552656465656d3a2072656465656d20656e74727920646f6573206e6f7420657869737472656465656d3a206573546f6b656e416d6f756e742063616e6e6f74206265206e756c6c66696e616c697a6552656465656d3a2076657374696e67206475726174696f6e20686173206e6f7420656e646564207965745361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa164736f6c6343000706000a

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

0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c4000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004ab9070b7680f802cbf8322e597a4409902171e5000000000000000000000000000000000000000000000000000000000000000c457363726f77656420414c42000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056573414c42000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _underlyingToken (address): 0x1dd2d631c92b1aCdFCDd51A0F7145A50130050C4
Arg [1] : name (string): Escrowed ALB
Arg [2] : symbol (string): esALB
Arg [3] : _treasuryAddress (address): 0x4aB9070B7680f802cBf8322e597a4409902171e5

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000001dd2d631c92b1acdfcdd51a0f7145a50130050c4
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [3] : 0000000000000000000000004ab9070b7680f802cbf8322e597a4409902171e5
Arg [4] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [5] : 457363726f77656420414c420000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [7] : 6573414c42000000000000000000000000000000000000000000000000000000


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.