ETH Price: $3,413.02 (+1.63%)
Gas: 7 Gwei

Contract

0x3952787d8Ec653E9179a3Fa5D8c018d7bD8e94c7
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040145474592022-04-08 21:11:50829 days ago1649452310IN
 Create: Unlock
0 ETH0.1430476342

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Unlock

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 80 runs

Other Settings:
default evmVersion
File 1 of 18 : Unlock.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

/**
 * @title The Unlock contract
 * @author Julien Genestoux (unlock-protocol.com)
 * This smart contract has 3 main roles:
 *  1. Distribute discounts to discount token holders
 *  2. Grant dicount tokens to users making referrals and/or publishers granting discounts.
 *  3. Create & deploy Public Lock contracts.
 * In order to achieve these 3 elements, it keeps track of several things such as
 *  a. Deployed locks addresses and balances of discount tokens granted by each lock.
 *  b. The total network product (sum of all key sales, net of discounts)
 *  c. Total of discounts granted
 *  d. Balances of discount tokens, including 'frozen' tokens (which have been used to claim
 * discounts and cannot be used/transferred for a given period)
 *  e. Growth rate of Network Product
 *  f. Growth rate of Discount tokens supply
 * The smart contract has an owner who only can perform the following
 *  - Upgrades
 *  - Change in golden rules (20% of GDP available in discounts, and supply growth rate is at most
 * 50% of GNP growth rate)
 * NOTE: This smart contract is partially implemented for now until enough Locks are deployed and
 * in the wild.
 * The partial implementation includes the following features:
 *  a. Keeping track of deployed locks
 *  b. Keeping track of GNP
 */

import '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';
import '@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol';
import 'hardlydifficult-eth/contracts/protocols/Uniswap/IUniswapOracle.sol';
import './utils/UnlockOwnable.sol';
import './utils/UnlockInitializable.sol';
import './interfaces/IPublicLock.sol';
import './interfaces/IMintableERC20.sol';

/// @dev Must list the direct base contracts in the order from “most base-like” to “most derived”.
/// https://solidity.readthedocs.io/en/latest/contracts.html#multiple-inheritance-and-linearization
contract Unlock is
  UnlockInitializable,
  UnlockOwnable
{

  /**
   * The struct for a lock
   * We use deployed to keep track of deployments.
   * This is required because both totalSales and yieldedDiscountTokens are 0 when initialized,
   * which would be the same values when the lock is not set.
   */
  struct LockBalances
  {
    bool deployed;
    uint totalSales; // This is in wei
    uint yieldedDiscountTokens;
  }

  modifier onlyFromDeployedLock() {
    require(locks[msg.sender].deployed, 'ONLY_LOCKS');
    _;
  }

  uint public grossNetworkProduct;

  uint public totalDiscountGranted;

  // We keep track of deployed locks to ensure that callers are all deployed locks.
  mapping (address => LockBalances) public locks;

  // global base token URI
  // Used by locks where the owner has not set a custom base URI.
  string public globalBaseTokenURI;

  // global base token symbol
  // Used by locks where the owner has not set a custom symbol
  string public globalTokenSymbol;

  // The address of the latest public lock template, used by default when `createLock` is called
  address public publicLockAddress;

  // Map token address to oracle contract address if the token is supported
  // Used for GDP calculations
  mapping (address => IUniswapOracle) public uniswapOracles;

  // The WETH token address, used for value calculations
  address public weth;

  // The UDT token address, used to mint tokens on referral
  address public udt;

  // The approx amount of gas required to purchase a key
  uint public estimatedGasForPurchase;

  // Blockchain ID the network id on which this version of Unlock is operating
  uint public chainId;

  // store proxy admin
  address public proxyAdminAddress;
  ProxyAdmin private proxyAdmin;

  // publicLock templates
  mapping(address => uint16) private _publicLockVersions;
  mapping(uint16 => address) private _publicLockImpls;
  uint16 public publicLockLatestVersion;

  // Events
  event NewLock(
    address indexed lockOwner,
    address indexed newLockAddress
  );

  event LockUpgraded(
    address lockAddress,
    uint16 version
  );

  event ConfigUnlock(
    address udt,
    address weth,
    uint estimatedGasForPurchase,
    string globalTokenSymbol,
    string globalTokenURI,
    uint chainId
  );

  event SetLockTemplate(
    address publicLockAddress
  );

  event GNPChanged(
    uint grossNetworkProduct,
    uint _valueInETH,
    address tokenAddress,
    uint value,
    address lockAddress
  );
  
  event ResetTrackedValue(
    uint grossNetworkProduct,
    uint totalDiscountGranted
  );

  event UnlockTemplateAdded(
    address indexed impl,
    uint16 indexed version
  );

  // Use initialize instead of a constructor to support proxies (for upgradeability via OZ).
  function initialize(
    address _unlockOwner
  )
    public
    initializer()
  {
    // We must manually initialize Ownable
    UnlockOwnable.__initializeOwnable(_unlockOwner);
    // add a proxy admin on deployment
    _deployProxyAdmin();
  }

  function initializeProxyAdmin() public onlyOwner {
    require(proxyAdminAddress == address(0), "ALREADY_DEPLOYED");
    _deployProxyAdmin();
  }

  /**
  * @dev Deploy the ProxyAdmin contract that will manage lock templates upgrades
  * This deploys an instance of ProxyAdmin used by PublicLock transparent proxies.
  */
  function _deployProxyAdmin() private returns(address) {
    proxyAdmin = new ProxyAdmin();
    proxyAdminAddress = address(proxyAdmin);
    return address(proxyAdmin);
  }

  /**
  * @dev Helper to get the version number of a template from his address
  */
  function publicLockVersions(address _impl) external view returns(uint16) {
    return _publicLockVersions[_impl];
  }

  /**
  * @dev Helper to get the address of a template based on its version number
  */
  function publicLockImpls(uint16 _version) external view returns(address) {
    return _publicLockImpls[_version];
  }

  /**
  * @dev Registers a new PublicLock template immplementation
  * The template is identified by a version number
  * Once registered, the template can be used to upgrade an existing Lock
  */
  function addLockTemplate(address impl, uint16 version) public onlyOwner {
    _publicLockVersions[impl] = version;
    _publicLockImpls[version] = impl;
    if (publicLockLatestVersion < version) publicLockLatestVersion = version;

    emit UnlockTemplateAdded(impl, version);
  }

  /**
  * @notice Create lock (legacy)
  * This deploys a lock for a creator. It also keeps track of the deployed lock.
  * @param _expirationDuration the duration of the lock (pass type(uint).max for unlimited duration)
  * @param _tokenAddress set to the ERC20 token address, or 0 for ETH.
  * @param _keyPrice the price of each key
  * @param _maxNumberOfKeys the maximum nimbers of keys to be edited
  * @param _lockName the name of the lock
  * param _salt [deprec] -- kept only for backwards copatibility
  * This may be implemented as a sequence ID or with RNG. It's used with `create2`
  * to know the lock's address before the transaction is mined.
  * @dev internally call `createUpgradeableLock`
  */
  function createLock(
    uint _expirationDuration,
    address _tokenAddress,
    uint _keyPrice,
    uint _maxNumberOfKeys,
    string calldata _lockName,
    bytes12 // _salt
  ) public returns(address) {

    bytes memory data = abi.encodeWithSignature(
      'initialize(address,uint256,address,uint256,uint256,string)',
      msg.sender,
      _expirationDuration,
      _tokenAddress,
      _keyPrice,
      _maxNumberOfKeys,
      _lockName
    );

    return createUpgradeableLock(data);
  }

  /**
  * @notice Create upgradeable lock
  * This deploys a lock for a creator. It also keeps track of the deployed lock.
  * @param data bytes containing the call to initialize the lock template
  * @dev this call is passed as encoded function - for instance:
  *  bytes memory data = abi.encodeWithSignature(
  *    'initialize(address,uint256,address,uint256,uint256,string)',
  *    msg.sender,
  *    _expirationDuration,
  *    _tokenAddress,
  *    _keyPrice,
  *    _maxNumberOfKeys,
  *    _lockName
  *  );
  * @return address of the create lock
  */
  function createUpgradeableLock(
    bytes memory data
  ) public returns(address)
  {
    address newLock = createUpgradeableLockAtVersion(data, publicLockLatestVersion);
    return newLock;
  }

  /**
   * Create an upgradeable lock using a specific PublicLock version
   * @param data bytes containing the call to initialize the lock template
   * (refer to createUpgradeableLock for more details)
   * @param _lockVersion the version of the lock to use
  */
  function createUpgradeableLockAtVersion(
    bytes memory data,
    uint16 _lockVersion
  ) public returns (address) {
    require(proxyAdminAddress != address(0), "MISSING_PROXY_ADMIN");

    // get lock version
    address publicLockImpl = _publicLockImpls[_lockVersion];
    require(publicLockImpl != address(0), 'MISSING_LOCK_TEMPLATE');

    // deploy a proxy pointing to impl
    TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(publicLockImpl, proxyAdminAddress, data);
    address payable newLock = payable(address(proxy));

    // assign the new Lock
    locks[newLock] = LockBalances({
      deployed: true, totalSales: 0, yieldedDiscountTokens: 0
    });

    // trigger event
    emit NewLock(msg.sender, newLock);
    return newLock;
  }

  /**
   * @dev Upgrade a Lock template implementation
   * @param lockAddress the address of the lock to be upgraded
   * @param version the version number of the template
   */
  function upgradeLock(address payable lockAddress, uint16 version) external returns(address) {
    require(proxyAdminAddress != address(0), "MISSING_PROXY_ADMIN");

    // check perms
    require(_isLockManager(lockAddress, msg.sender) == true, "MANAGER_ONLY");

    // check version
    IPublicLock lock = IPublicLock(lockAddress);
    uint16 currentVersion = lock.publicLockVersion();
    require( version == currentVersion + 1, 'VERSION_TOO_HIGH');

    // make our upgrade
    address impl = _publicLockImpls[version];
    require(impl != address(0), "MISSING_TEMPLATE");

    TransparentUpgradeableProxy proxy = TransparentUpgradeableProxy(lockAddress);
    proxyAdmin.upgrade(proxy, impl);

    // let's upgrade the data schema
    // the function is called with empty bytes as migration behaviour is set by the lock in accordance to data version
    lock.migrate('0x');

    emit LockUpgraded(lockAddress, version);
    return lockAddress;
  }

  function _isLockManager(address lockAddress, address _sender) private view returns(bool isManager) {
    IPublicLock lock = IPublicLock(lockAddress);
    return lock.isLockManager(_sender);
  }

  /**
   * @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
   * @dev [DEPRECATED] Kept for backwards compatibility
   */
  function computeAvailableDiscountFor(
    address /* _purchaser */,
    uint /* _keyPrice */
  )
    public
    pure
    returns (uint discount, uint tokens)
  {
    return (0, 0);
  }

  /**
   * Helper to get the network mining basefee as introduced in EIP-1559
   * @dev this helper can be wrapped in try/catch statement to avoid 
   * revert in networks where EIP-1559 is not implemented
   */
  function networkBaseFee() external view returns (uint) {
    return block.basefee;
  }

  /**
   * This function keeps track of the added GDP, as well as grants of discount tokens
   * to the referrer, if applicable.
   * The number of discount tokens granted is based on the value of the referal,
   * the current growth rate and the lock's discount token distribution rate
   * This function is invoked by a previously deployed lock only.
   * TODO: actually implement
   */
  function recordKeyPurchase(
    uint _value,
    address _referrer
  )
    public
    onlyFromDeployedLock()
  {
    if(_value > 0) {
      uint valueInETH;
      address tokenAddress = IPublicLock(msg.sender).tokenAddress();
      if(tokenAddress != address(0) && tokenAddress != weth) {
        // If priced in an ERC-20 token, find the supported uniswap oracle
        IUniswapOracle oracle = uniswapOracles[tokenAddress];
        if(address(oracle) != address(0)) {
          valueInETH = oracle.updateAndConsult(tokenAddress, _value, weth);
        }
      }
      else {
        // If priced in ETH (or value is 0), no conversion is required
        valueInETH = _value;
      }

      updateGrossNetworkProduct(
        valueInETH,
        tokenAddress,
        _value,
        msg.sender // lockAddress
      );

      // If GNP does not overflow, the lock totalSales should be safe
      locks[msg.sender].totalSales += valueInETH;

      // Mint UDT
      if(_referrer != address(0))
      {
        IUniswapOracle udtOracle = uniswapOracles[udt];
        if(address(udtOracle) != address(0))
        {
          // Get the value of 1 UDT (w/ 18 decimals) in ETH
          uint udtPrice = udtOracle.updateAndConsult(udt, 10 ** 18, weth);

          // base fee default to 100 GWEI for chains that does 
          uint baseFee;
          try this.networkBaseFee() returns (uint _basefee) {
            // no assigned value
            if(_basefee == 0) {
              baseFee = 100;
            } else {
              baseFee = _basefee;
            }
          } catch {
            // block.basefee not supported
            baseFee = 100;
          }

          // tokensToDistribute is either == to the gas cost times 1.25 to cover the 20% dev cut
          uint tokensToDistribute = (estimatedGasForPurchase * baseFee) * (125 * 10 ** 18) / 100 / udtPrice;

          // or tokensToDistribute is capped by network GDP growth
          uint maxTokens = 0;
          if (chainId > 1)
          {
            // non mainnet: we distribute tokens using asymptotic curve between 0 and 0.5
            // maxTokens = IMintableERC20(udt).balanceOf(address(this)).mul((valueInETH / grossNetworkProduct) / (2 + 2 * valueInETH / grossNetworkProduct));
            maxTokens = IMintableERC20(udt).balanceOf(address(this)) * valueInETH / (2 + 2 * valueInETH / grossNetworkProduct) / grossNetworkProduct;
          } else {
            // Mainnet: we mint new token using log curve
            maxTokens = IMintableERC20(udt).totalSupply() * valueInETH / 2 / grossNetworkProduct;
          }

          // cap to GDP growth!
          if(tokensToDistribute > maxTokens)
          {
            tokensToDistribute = maxTokens;
          }

          if(tokensToDistribute > 0)
          {
            // 80% goes to the referrer, 20% to the Unlock dev - round in favor of the referrer
            uint devReward = tokensToDistribute * 20 / 100;
            if (chainId > 1)
            {
              uint balance = IMintableERC20(udt).balanceOf(address(this));
              if (balance > tokensToDistribute) {
                // Only distribute if there are enough tokens
                IMintableERC20(udt).transfer(_referrer, tokensToDistribute - devReward);
                IMintableERC20(udt).transfer(owner(), devReward);
              }
            } else {
              // No distribnution
              IMintableERC20(udt).mint(_referrer, tokensToDistribute - devReward);
              IMintableERC20(udt).mint(owner(), devReward);
            }
          }
        }
      }
    }
  }

  /**
   * Update the GNP by a new value. 
   * Emits an event to simply tracking
   */
  function updateGrossNetworkProduct(
    uint _valueInETH,
    address _tokenAddress,
    uint _value,
    address _lock
  ) internal {
    // increase GNP
    grossNetworkProduct = grossNetworkProduct + _valueInETH;

    emit GNPChanged(
      grossNetworkProduct,
      _valueInETH,
      _tokenAddress,
      _value,
      _lock
    );
  }

  /**
   * @notice [DEPRECATED] Call to this function has been removed from PublicLock > v9.
   * @dev [DEPRECATED] only Kept for backwards compatibility
   */
  function recordConsumedDiscount(
    uint /* _discount */,
    uint /* _tokens */
  )
    public
    view
    onlyFromDeployedLock()
  {
    return;
  }

  // The version number of the current Unlock implementation on this network
  function unlockVersion(
  ) external pure
    returns (uint16)
  {
    return 11;
  }

  /**
   * @notice Allows the owner to update configuration variables
   */
  function configUnlock(
    address _udt,
    address _weth,
    uint _estimatedGasForPurchase,
    string calldata _symbol,
    string calldata _URI,
    uint _chainId
  ) external
    onlyOwner
  {
    udt = _udt;
    weth = _weth;
    estimatedGasForPurchase = _estimatedGasForPurchase;

    globalTokenSymbol = _symbol;
    globalBaseTokenURI = _URI;

    chainId = _chainId;

    emit ConfigUnlock(_udt, _weth, _estimatedGasForPurchase, _symbol, _URI, _chainId);
  }

  /**
   * @notice Upgrade the PublicLock template used for future calls to `createLock`.
   * @dev This will initialize the template and revokeOwnership.
   */
  function setLockTemplate(
    address _publicLockAddress
  ) external
    onlyOwner
  {
    // First claim the template so that no-one else could
    // this will revert if the template was already initialized.
    IPublicLock(_publicLockAddress).initialize(
      address(this), 0, address(0), 0, 0, ''
    );
    IPublicLock(_publicLockAddress).renounceLockManager();

    publicLockAddress = _publicLockAddress;

    emit SetLockTemplate(_publicLockAddress);
  }

  /**
   * @notice allows the owner to set the oracle address to use for value conversions
   * setting the _oracleAddress to address(0) removes support for the token
   * @dev This will also call update to ensure at least one datapoint has been recorded.
   */
  function setOracle(
    address _tokenAddress,
    address _oracleAddress
  ) external
    onlyOwner
  {
    uniswapOracles[_tokenAddress] = IUniswapOracle(_oracleAddress);
    if(_oracleAddress != address(0)) {
      IUniswapOracle(_oracleAddress).update(_tokenAddress, weth);
    }
  }

  // Allows the owner to change the value tracking variables as needed.
  function resetTrackedValue(
    uint _grossNetworkProduct,
    uint _totalDiscountGranted
  ) external
    onlyOwner
  {
    grossNetworkProduct = _grossNetworkProduct;
    totalDiscountGranted = _totalDiscountGranted;

    emit ResetTrackedValue(_grossNetworkProduct, _totalDiscountGranted);
  }

  /**
   * @dev Redundant with globalBaseTokenURI() for backwards compatibility with v3 & v4 locks.
   */
  function getGlobalBaseTokenURI()
    external
    view
    returns (string memory)
  {
    return globalBaseTokenURI;
  }

  /**
   * @dev Redundant with globalTokenSymbol() for backwards compatibility with v3 & v4 locks.
   */
  function getGlobalTokenSymbol()
    external
    view
    returns (string memory)
  {
    return globalTokenSymbol;
  }
}

File 2 of 18 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967Proxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _changeAdmin(admin_);
    }

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _getAdmin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        _changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeToAndCall(newImplementation, bytes(""), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
        _upgradeToAndCall(newImplementation, data, true);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address) {
        return _getAdmin();
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 3 of 18 : ProxyAdmin.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)

pragma solidity ^0.8.0;

import "./TransparentUpgradeableProxy.sol";
import "../../access/Ownable.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {
    /**
     * @dev Returns the current implementation of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Returns the current admin of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("admin()")) == 0xf851a440
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Changes the admin of `proxy` to `newAdmin`.
     *
     * Requirements:
     *
     * - This contract must be the current admin of `proxy`.
     */
    function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
        proxy.changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
        proxy.upgradeTo(implementation);
    }

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(
        TransparentUpgradeableProxy proxy,
        address implementation,
        bytes memory data
    ) public payable virtual onlyOwner {
        proxy.upgradeToAndCall{value: msg.value}(implementation, data);
    }
}

File 4 of 18 : IUniswapOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

interface IUniswapOracle
{
    function PERIOD() external returns (uint);
    function factory() external returns (address);
    function update(
      address _tokenIn,
      address _tokenOut
    ) external;
    function consult(
      address _tokenIn,
      uint _amountIn,
      address _tokenOut
    ) external view
      returns (uint _amountOut);
    function updateAndConsult(
      address _tokenIn,
      uint _amountIn,
      address _tokenOut
    ) external
      returns (uint _amountOut);
}

File 5 of 18 : UnlockOwnable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (access/Ownable.sol)

pragma solidity ^0.8.0;

import './UnlockInitializable.sol';
import './UnlockContextUpgradeable.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.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be aplied to your functions to restrict their use to
 * the owner.
 * 
 * This contract was originally part of openzeppelin/contracts-ethereum-package
 * but had to be included (instead of using the one in openzeppelin/contracts-upgradeable ) 
 * because the ______gap array length was 49 instead of 50
 */
abstract contract UnlockOwnable is UnlockInitializable, UnlockContextUpgradeable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __initializeOwnable(address sender) public initializer {
        _owner = sender;
        emit OwnershipTransferred(address(0), _owner);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "ONLY_OWNER");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _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 onlyOwner {
        _transferOwnership(address(0));
    }

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

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

    uint256[50] private ______gap;
}

File 6 of 18 : UnlockInitializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol';

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract UnlockInitializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(initializing ? _isConstructor() : !initialized, "ALREADY_INITIALIZED");

        bool isTopLevelCall = !initializing;
        if (isTopLevelCall) {
            initializing = true;
            initialized = true;
        }

        _;

        if (isTopLevelCall) {
            initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(initializing, "NOT_INITIALIZING");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 7 of 18 : IPublicLock.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.17 <0.9.0;
pragma experimental ABIEncoderV2;

/**
* @title The PublicLock Interface
* @author Nick Furfaro (unlock-protocol.com)
 */


interface IPublicLock
{

// See indentationissue description here:
// https://github.com/duaraghav8/Ethlint/issues/268
// solium-disable indentation

  /// Functions
  function initialize(
    address _lockCreator,
    uint _expirationDuration,
    address _tokenAddress,
    uint _keyPrice,
    uint _maxNumberOfKeys,
    string calldata _lockName
  ) external;

  /**
   * @notice Allow the contract to accept tips in ETH sent directly to the contract.
   * @dev This is okay to use even if the lock is priced in ERC-20 tokens
   */
  // receive() external payable;

  // roles
  function DEFAULT_ADMIN_ROLE() external pure returns (bytes32);
  function KEY_GRANTER_ROLE() external pure returns (bytes32);
  function LOCK_MANAGER_ROLE() external pure returns (bytes32);

  /**
  * @notice The version number of the current implementation on this network.
  * @return The current version number.
  */
  function publicLockVersion() external pure returns (uint16);

  /**
   * @dev Called by a lock manager or beneficiary to withdraw all funds from the lock and send them to the `beneficiary`.
   * @dev Throws if called by other than a lock manager or beneficiary
   * @param _tokenAddress specifies the token address to withdraw or 0 for ETH. This is usually
   * the same as `tokenAddress` in MixinFunds.
   * @param _amount specifies the max amount to withdraw, which may be reduced when
   * considering the available balance. Set to 0 or MAX_UINT to withdraw everything.
   *  -- however be wary of draining funds as it breaks the `cancelAndRefund` and `expireAndRefundFor`
   * use cases.
   */
  function withdraw(
    address _tokenAddress,
    uint _amount
  ) external;

  /**
   * @notice An ERC-20 style approval, allowing the spender to transfer funds directly from this lock.
   */
  function approveBeneficiary(
    address _spender,
    uint _amount
  ) external
    returns (bool);

  /**
   * A function which lets a Lock manager of the lock to change the price for future purchases.
   * @dev Throws if called by other than a Lock manager
   * @dev Throws if lock has been disabled
   * @dev Throws if _tokenAddress is not a valid token
   * @param _keyPrice The new price to set for keys
   * @param _tokenAddress The address of the erc20 token to use for pricing the keys,
   * or 0 to use ETH
   */
  function updateKeyPricing( uint _keyPrice, address _tokenAddress ) external;

  /**
   * A function to change the default duration of each key in the lock
   * @notice keys previously bought are unaffected by this change (i.e.
   * existing keys timestamps are not recalculated/updated)
   * @param _newExpirationDuration the new amount of time for each key purchased 
   * or type(uint).max for a non-expiring key
   */
  function setExpirationDuration(uint _newExpirationDuration) external;

  /**
   * A function which lets a Lock manager update the beneficiary account,
   * which receives funds on withdrawal.
   * @dev Throws if called by other than a Lock manager or beneficiary
   * @dev Throws if _beneficiary is address(0)
   * @param _beneficiary The new address to set as the beneficiary
   */
  function updateBeneficiary( address _beneficiary ) external;

  /**
   * Checks if the user has a non-expired key.
   * @param _user The address of the key owner
   */
  function getHasValidKey(
    address _user
  ) external view returns (bool);

  /**
  * @dev Returns the key's ExpirationTimestamp field for a given owner.
  * @param _tokenId the id of the key
  * @dev Returns 0 if the owner has never owned a key for this lock
  */
  function keyExpirationTimestampFor(
    uint _tokenId
  ) external view returns (uint timestamp);
  
  /**
   * Public function which returns the total number of unique owners (both expired
   * and valid).  This may be larger than totalSupply.
   */
  function numberOfOwners() external view returns (uint);

  /**
   * Allows a Lock manager to assign a descriptive name for this Lock.
   * @param _lockName The new name for the lock
   * @dev Throws if called by other than a Lock manager
   */
  function updateLockName(
    string calldata _lockName
  ) external;

  /**
   * Allows a Lock manager to assign a Symbol for this Lock.
   * @param _lockSymbol The new Symbol for the lock
   * @dev Throws if called by other than a Lock manager
   */
  function updateLockSymbol(
    string calldata _lockSymbol
  ) external;

  /**
    * @dev Gets the token symbol
    * @return string representing the token symbol
    */
  function symbol()
    external view
    returns(string memory);

    /**
   * Allows a Lock manager to update the baseTokenURI for this Lock.
   * @dev Throws if called by other than a Lock manager
   * @param _baseTokenURI String representing the base of the URI for this lock.
   */
  function setBaseTokenURI(
    string calldata _baseTokenURI
  ) external;

  /**  @notice A distinct Uniform Resource Identifier (URI) for a given asset.
   * @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
   *  3986. The URI may point to a JSON file that conforms to the "ERC721
   *  Metadata JSON Schema".
   * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
   * @param _tokenId The tokenID we're inquiring about
   * @return String representing the URI for the requested token
   */
  function tokenURI(
    uint256 _tokenId
  ) external view returns(string memory);

  /**
   * @notice Allows a Lock manager to add or remove an event hook
   */
  function setEventHooks(
    address _onKeyPurchaseHook,
    address _onKeyCancelHook,
    address _onValidKeyHook,
    address _onTokenURIHook
  ) external;

  /**
   * Allows a Lock manager to give a collection of users a key with no charge.
   * Each key may be assigned a different expiration date.
   * @dev Throws if called by other than a Lock manager
   * @param _recipients An array of receiving addresses
   * @param _expirationTimestamps An array of expiration Timestamps for the keys being granted
   */
  function grantKeys(
    address[] calldata _recipients,
    uint[] calldata _expirationTimestamps,
    address[] calldata _keyManagers
  ) external;

  /**
  * @dev Purchase function
  * @param _values array of tokens amount to pay for this purchase >= the current keyPrice - any applicable discount
  * (_values is ignored when using ETH)
  * @param _recipients array of addresses of the recipients of the purchased key
  * @param _referrers array of addresses of the users making the referral
  * @param _keyManagers optional array of addresses to grant managing rights to a specific address on creation
  * @param _data array of arbitrary data populated by the front-end which initiated the sale
  * @notice when called for an existing and non-expired key, the `_keyManager` param will be ignored 
  * @dev Setting _value to keyPrice exactly doubles as a security feature. That way if the lock owner increases the
  * price while my transaction is pending I can't be charged more than I expected (only applicable to ERC-20 when more
  * than keyPrice is approved for spending).
  */
  function purchase(
    uint256[] calldata _values,
    address[] calldata _recipients,
    address[] calldata _referrers,
    address[] calldata _keyManagers,
    bytes[] calldata _data
  ) external payable;
  
  /**
  * @dev Extend function
  * @param _value the number of tokens to pay for this purchase >= the current keyPrice - any applicable discount
  * (_value is ignored when using ETH)
  * @param _tokenId the id of the key to extend
  * @param _referrer address of the user making the referral
  * @param _data arbitrary data populated by the front-end which initiated the sale
  * @dev Throws if lock is disabled or key does not exist for _recipient. Throws if _recipient == address(0).
  */
  function extend(
    uint _value,
    uint _tokenId,
    address _referrer,
    bytes calldata _data
  ) external payable;

  /**
   * Merge existing keys
   * @param _tokenIdFrom the id of the token to substract time from
   * @param _tokenIdTo the id of the destination token  to add time
   * @param _amount the amount of time to transfer (in seconds)
   */
  function mergeKeys(uint _tokenIdFrom, uint _tokenIdTo, uint _amount) external;

  /**
   * Deactivate an existing key
   * @param _tokenId the id of token to burn
   * @notice the key will be expired and ownership records will be destroyed
   */
  function burn(uint _tokenId) external;

  /**
  * @param _gasRefundValue price in wei or token in smallest price unit
  * @dev Set the value to be refunded to the sender on purchase
  */
  function setGasRefundValue(uint256 _gasRefundValue) external;
  
  /**
  * _gasRefundValue price in wei or token in smallest price unit
  * @dev Returns the value/rpice to be refunded to the sender on purchase
  */
  function gasRefundValue() external view returns (uint256 _gasRefundValue);

  /**
   * @notice returns the minimum price paid for a purchase with these params.
   * @dev this considers any discount from Unlock or the OnKeyPurchase hook.
   */
  function purchasePriceFor(
    address _recipient,
    address _referrer,
    bytes calldata _data
  ) external view
    returns (uint);

  /**
   * Allow a Lock manager to change the transfer fee.
   * @dev Throws if called by other than a Lock manager
   * @param _transferFeeBasisPoints The new transfer fee in basis-points(bps).
   * Ex: 200 bps = 2%
   */
  function updateTransferFee(
    uint _transferFeeBasisPoints
  ) external;

  /**
   * Determines how much of a fee would need to be paid in order to
   * transfer to another account.  This is pro-rated so the fee goes 
   * down overtime.
   * @dev Throws if _tokenId does not have a valid key
   * @param _tokenId The id of the key check the transfer fee for.
   * @param _time The amount of time to calculate the fee for.
   * @return The transfer fee in seconds.
   */
  function getTransferFee(
    uint _tokenId,
    uint _time
  ) external view returns (uint);

  /**
   * @dev Invoked by a Lock manager to expire the user's key 
   * and perform a refund and cancellation of the key
   * @param _tokenId The key id we wish to refund to
   * @param _amount The amount to refund to the key-owner
   * @dev Throws if called by other than a Lock manager
   * @dev Throws if _keyOwner does not have a valid key
   */
  function expireAndRefundFor(
    uint _tokenId,
    uint _amount
  ) external;

   /**
   * @dev allows the key manager to expire a given tokenId
   * and send a refund to the keyOwner based on the amount of time remaining.
   * @param _tokenId The id of the key to cancel.
   */
  function cancelAndRefund(uint _tokenId) external;

  /**
   * Allow a Lock manager to change the refund penalty.
   * @dev Throws if called by other than a Lock manager
   * @param _freeTrialLength The new duration of free trials for this lock
   * @param _refundPenaltyBasisPoints The new refund penaly in basis-points(bps)
   */
  function updateRefundPenalty(
    uint _freeTrialLength,
    uint _refundPenaltyBasisPoints
  ) external;

  /**
   * @dev Determines how much of a refund a key owner would receive if they issued
   * @param _keyOwner The key owner to get the refund value for.
   * a cancelAndRefund block.timestamp.
   * Note that due to the time required to mine a tx, the actual refund amount will be lower
   * than what the user reads from this call.
   */
  function getCancelAndRefundValue(
    address _keyOwner
  ) external view returns (uint refund);

  function addKeyGranter(address account) external;

  function addLockManager(address account) external;

  function isKeyGranter(address account) external view returns (bool);

  function isLockManager(address account) external view returns (bool);

  function onKeyPurchaseHook() external view returns(address);

  function onKeyCancelHook() external view returns(address);
  
  function onValidKeyHook() external view returns(bool);

  function onTokenURIHook() external view returns(string memory);

  function revokeKeyGranter(address _granter) external;

  function renounceLockManager() external;

  /**
   * @dev Change the maximum number of keys the lock can edit
   * @param _maxNumberOfKeys uint the maximum number of keys
   */
  function setMaxNumberOfKeys (uint _maxNumberOfKeys) external;

   /**
   * Set the maximum number of keys a specific address can use
   * @param _maxKeysPerAddress the maximum amount of key a user can own
   */
  function setMaxKeysPerAddress (uint _maxKeysPerAddress) external;

  /**
   * @return the maximum number of key allowed for a single address
   */
  function maxKeysPerAddress() external view returns (uint);


  ///===================================================================
  /// Auto-generated getter functions from public state variables

  function beneficiary() external view returns (address );

  function expirationDuration() external view returns (uint256 );

  function freeTrialLength() external view returns (uint256 );

  function keyPrice() external view returns (uint256 );

  function maxNumberOfKeys() external view returns (uint256 );

  function refundPenaltyBasisPoints() external view returns (uint256 );

  function tokenAddress() external view returns (address );

  function transferFeeBasisPoints() external view returns (uint256 );

  function unlockProtocol() external view returns (address );

  function keyManagerOf(uint) external view returns (address );

  ///===================================================================

  /**
  * @notice Allows the key owner to safely share their key (parent key) by
  * transferring a portion of the remaining time to a new key (child key).
  * @dev Throws if key is not valid.
  * @dev Throws if `_to` is the zero address
  * @param _to The recipient of the shared key
  * @param _tokenId the key to share
  * @param _timeShared The amount of time shared
  * checks if `_to` is a smart contract (code size > 0). If so, it calls
  * `onERC721Received` on `_to` and throws if the return value is not
  * `bytes4(keccak256('onERC721Received(address,address,uint,bytes)'))`.
  * @dev Emit Transfer event
  */
  function shareKey(
    address _to,
    uint _tokenId,
    uint _timeShared
  ) external;

  /**
  * @notice Update transfer and cancel rights for a given key
  * @param _tokenId The id of the key to assign rights for
  * @param _keyManager The address to assign the rights to for the given key
  */
  function setKeyManagerOf(
    uint _tokenId,
    address _keyManager
  ) external;
  
  /**
  * Check if a certain key is valid
  * @param _tokenId the id of the key to check validity
  * @notice this makes use of the onValidKeyHook if it is set
  */
  function isValidKey(
    uint _tokenId
  )
    external
    view
    returns (bool);
  
  /// @notice A descriptive name for a collection of NFTs in this contract
  function name() external view returns (string memory _name);
  ///===================================================================

  /// From ERC165.sol
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
  ///===================================================================

  /// From ERC-721
  /**
    * @dev Returns the number of NFTs in `owner`'s account.
    */
  function balanceOf(address _owner) external view returns (uint256 balance);

  /**
    * @dev Returns the owner of the NFT specified by `tokenId`.
    */
  function ownerOf(uint256 tokenId) external view returns (address _owner);

  /**
    * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
    * another (`to`).
    *
    * Requirements:
    * - `from`, `to` cannot be zero.
    * - `tokenId` must be owned by `from`.
    * - If the caller is not `from`, it must be have been allowed to move this
    * NFT by either {approve} or {setApprovalForAll}.
    */
  function safeTransferFrom(address from, address to, uint256 tokenId) external;
  
  /**
    * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
    * another (`to`).
    *
    * Requirements:
    * - If the caller is not `from`, it must be approved to move this NFT by
    * either {approve} or {setApprovalForAll}.
    */
  function transferFrom(address from, address to, uint256 tokenId) external;
  function approve(address to, uint256 tokenId) external;

  /**
    * @notice Get the approved address for a single NFT
    * @dev Throws if `_tokenId` is not a valid NFT.
    * @param _tokenId The NFT to find the approved address for
    * @return operator The approved address for this NFT, or the zero address if there is none
    */
  function getApproved(uint256 _tokenId) external view returns (address operator);

   /**
   * @dev Sets or unsets the approval of a given operator
   * An operator is allowed to transfer all tokens of the sender on their behalf
   * @param _operator operator address to set the approval
   * @param _approved representing the status of the approval to be set
   * @notice disabled when transfers are disabled
   */
  function setApprovalForAll(address _operator, bool _approved) external;

   /**
   * @dev Tells whether an operator is approved by a given keyManager
   * @param _owner owner address which you want to query the approval of
   * @param _operator operator address which you want to query the approval of
   * @return bool whether the given operator is approved by the given owner
   */
  function isApprovedForAll(address _owner, address _operator) external view returns (bool);

  function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

  function totalSupply() external view returns (uint256);
  function tokenOfOwnerByIndex(address _owner, uint256 index) external view returns (uint256 tokenId);

  function tokenByIndex(uint256 index) external view returns (uint256);

  /**
    * Innherited from Open Zeppelin AccessControl.sol
    */
  function getRoleAdmin(bytes32 role) external view returns (bytes32);
  function grantRole(bytes32 role, address account) external;
  function revokeRole(bytes32 role, address account) external;
  function renounceRole(bytes32 role, address account) external;
  function hasRole(bytes32 role, address account) external view returns (bool);

  /**
    * @notice An ERC-20 style transfer.
    * @param _value sends a token with _value * expirationDuration (the amount of time remaining on a standard purchase).
    * @dev The typical use case would be to call this with _value 1, which is on par with calling `transferFrom`. If the user
    * has more than `expirationDuration` time remaining this may use the `shareKey` function to send some but not all of the token.
    */
  function transfer(
    address _to,
    uint _value
  ) external
    returns (bool success);

  /** `owner()` is provided as an helper to mimick the `Ownable` contract ABI.
    * The `Ownable` logic is used by many 3rd party services to determine
    * contract ownership - e.g. who is allowed to edit metadata on Opensea.
    * 
    * @notice This logic is NOT used internally by the Unlock Protocol and is made 
    * available only as a convenience helper.
    */
  function owner() external view returns (address);
  function setOwner(address account) external;
  function isOwner(address account) external returns (bool);

  /**
  * Migrate data from the previous single owner => key mapping to 
  * the new data structure w multiple tokens.
  * @param _calldata an ABI-encoded representation of the params (v10: the number of records to migrate as `uint`)
  * @dev when all record schemas are sucessfully upgraded, this function will update the `schemaVersion`
  * variable to the latest/current lock version
  */
  function migrate(bytes calldata _calldata) external;

  /**
  * Returns the version number of the data schema currently used by the lock
  * @notice if this is different from `publicLockVersion`, then the ability to purchase, grant
  * or extend keys is disabled.
  * @dev will return 0 if no ;igration has ever been run
  */
  function schemaVersion() external view returns (uint);

  /**
   * Set the schema version to the latest
   * @notice only lock manager call call this
   */
  function updateSchemaVersion() external;

    /**
  * Renew a given token
  * @notice only works for non-free, expiring, ERC20 locks
  * @param _tokenId the ID fo the token to renew
  * @param _referrer the address of the person to be granted UDT
  */
  function renewMembershipFor(
    uint _tokenId,
    address _referrer
  ) external;
}

File 8 of 18 : IMintableERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.17 <0.9.0;

interface IMintableERC20
{
  function mint(address account, uint256 amount) external returns (bool);
  function transfer(address recipient, uint256 amount) external returns (bool);
  function totalSupply() external returns (uint);
  function balanceOf(address account) external returns (uint256);
}

File 9 of 18 : ERC1967Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.0;

import "../Proxy.sol";
import "./ERC1967Upgrade.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _upgradeToAndCall(_logic, _data, false);
    }

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal view virtual override returns (address impl) {
        return ERC1967Upgrade._getImplementation();
    }
}

File 10 of 18 : Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Proxy.sol)

pragma solidity ^0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {}
}

File 11 of 18 : ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallSecure(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        address oldImplementation = _getImplementation();

        // Initial upgrade and setup call
        _setImplementation(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }

        // Perform rollback test if not already in progress
        StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
        if (!rollbackTesting.value) {
            // Trigger rollback using upgradeTo from the new implementation
            rollbackTesting.value = true;
            Address.functionDelegateCall(
                newImplementation,
                abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
            );
            rollbackTesting.value = false;
            // Check rollback was effective
            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
            // Finally reset to the new implementation and log the upgrade
            _upgradeTo(newImplementation);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}

File 12 of 18 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 13 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.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;
        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");

        (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");

        (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");

        (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");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 14 of 18 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly {
            r.slot := slot
        }
    }
}

File 15 of 18 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 16 of 18 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 17 of 18 : UnlockContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "./UnlockInitializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private ______gap;
}

File 18 of 18 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @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;
        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");

        (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");

        (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");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"udt","type":"address"},{"indexed":false,"internalType":"address","name":"weth","type":"address"},{"indexed":false,"internalType":"uint256","name":"estimatedGasForPurchase","type":"uint256"},{"indexed":false,"internalType":"string","name":"globalTokenSymbol","type":"string"},{"indexed":false,"internalType":"string","name":"globalTokenURI","type":"string"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"ConfigUnlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"grossNetworkProduct","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_valueInETH","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"address","name":"lockAddress","type":"address"}],"name":"GNPChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lockAddress","type":"address"},{"indexed":false,"internalType":"uint16","name":"version","type":"uint16"}],"name":"LockUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lockOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newLockAddress","type":"address"}],"name":"NewLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"grossNetworkProduct","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalDiscountGranted","type":"uint256"}],"name":"ResetTrackedValue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"publicLockAddress","type":"address"}],"name":"SetLockTemplate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"impl","type":"address"},{"indexed":true,"internalType":"uint16","name":"version","type":"uint16"}],"name":"UnlockTemplateAdded","type":"event"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"__initializeOwnable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"impl","type":"address"},{"internalType":"uint16","name":"version","type":"uint16"}],"name":"addLockTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"computeAvailableDiscountFor","outputs":[{"internalType":"uint256","name":"discount","type":"uint256"},{"internalType":"uint256","name":"tokens","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_udt","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"uint256","name":"_estimatedGasForPurchase","type":"uint256"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"string","name":"_URI","type":"string"},{"internalType":"uint256","name":"_chainId","type":"uint256"}],"name":"configUnlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_expirationDuration","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_keyPrice","type":"uint256"},{"internalType":"uint256","name":"_maxNumberOfKeys","type":"uint256"},{"internalType":"string","name":"_lockName","type":"string"},{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"createLock","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"createUpgradeableLock","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint16","name":"_lockVersion","type":"uint16"}],"name":"createUpgradeableLockAtVersion","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"estimatedGasForPurchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalBaseTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalTokenSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalBaseTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalTokenSymbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"grossNetworkProduct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_unlockOwner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initializeProxyAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"locks","outputs":[{"internalType":"bool","name":"deployed","type":"bool"},{"internalType":"uint256","name":"totalSales","type":"uint256"},{"internalType":"uint256","name":"yieldedDiscountTokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"networkBaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxyAdminAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicLockAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_version","type":"uint16"}],"name":"publicLockImpls","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicLockLatestVersion","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_impl","type":"address"}],"name":"publicLockVersions","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"recordConsumedDiscount","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"recordKeyPurchase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_grossNetworkProduct","type":"uint256"},{"internalType":"uint256","name":"_totalDiscountGranted","type":"uint256"}],"name":"resetTrackedValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_publicLockAddress","type":"address"}],"name":"setLockTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_oracleAddress","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDiscountGranted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"udt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"uniswapOracles","outputs":[{"internalType":"contract IUniswapOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlockVersion","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address payable","name":"lockAddress","type":"address"},{"internalType":"uint16","name":"version","type":"uint16"}],"name":"upgradeLock","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50613ccc806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200021d5760003560e01c80638da5cb5b1162000127578063c4d66de811620000b6578063c4d66de81462000503578063c7d7d0c9146200051a578063caeee3a31462000524578063ccf54648146200053b578063cd93b5321462000552578063cec410521462000561578063d6e9e803146200056b578063ef67fde61462000575578063f2fde38b146200058c578063f832899114620005a357600080fd5b80638da5cb5b146200046c5780638f32d59b146200047e578063939d9f1f1462000499578063963a947814620004b05780639a8a059214620004c7578063a082eb0e14620004d1578063a998e9fb14620004db578063b0ca0c8314620004e5578063ba5ea0d514620004fc57600080fd5b8063487a8a7611620001b0578063487a8a7614620003365780635979e755146200034d5780635c38eb3a14620003615780635de9a1371462000378578063666018a514620003cc5780636bced5a214620003fc578063715018a61462000428578063743bbc2f14620004325780637d8fb641146200044b5780637ff94bb2146200046257600080fd5b80630cb175e3146200022257806325772ad91462000253578063262d0a85146200029757806335a750de14620002ba5780633652466314620002d35780633fc8cef314620002ec5780634220bd46146200030057806342d9210614620003085780634452c2b2146200031f575b600080fd5b620002396200023336600462001ffe565b620005b7565b604080519283526020830191909152015b60405180910390f35b620002836200026436600462001e9d565b6001600160a01b031660009081526073602052604090205461ffff1690565b60405161ffff90911681526020016200024a565b606e54620002ab906001600160a01b031681565b6040516200024a91906200228d565b620002c4620005c2565b6040516200024a9190620023b5565b620002ea620002e4366004620021f1565b6200065c565b005b606d54620002ab906001600160a01b031681565b600b62000283565b620002ab6200031936600462002157565b6200069b565b620002ab6200033036600462002051565b62000701565b620002ab6200034736600462002089565b62000721565b607154620002ab906001600160a01b031681565b620002ea6200037236600462001f1b565b6200088e565b620003ae6200038936600462001e9d565b60686020526000908152604090208054600182015460029092015460ff909116919083565b6040805193151584526020840192909252908201526060016200024a565b620002ab620003dd366004620020d5565b61ffff166000908152607460205260409020546001600160a01b031690565b620002ab6200040d36600462001e9d565b606c602052600090815260409020546001600160a01b031681565b620002ea62000957565b6200043c606f5481565b6040519081526020016200024a565b620002ea6200045c36600462001f4e565b6200098e565b620002c462000a61565b6033546001600160a01b0316620002ab565b6200048862000a72565b60405190151581526020016200024a565b620002ea620004aa3660046200212f565b62000a83565b620002ea620004c1366004620021f1565b620012ee565b6200043c60705481565b6200043c60675481565b620002c46200135e565b620002ea620004f636600462001e9d565b620013f4565b486200043c565b620002ea6200051436600462001e9d565b620014b6565b620002ea62001545565b620002ea6200053536600462001edd565b620015c9565b620002ab6200054c36600462001edd565b62001697565b607554620002839061ffff1681565b620002c462001970565b6200043c60665481565b620002ea6200058636600462001e9d565b6200197f565b620002ea6200059d36600462001e9d565b62001ada565b606b54620002ab906001600160a01b031681565b6000805b9250929050565b6060606a8054620005d3906200250f565b80601f016020809104026020016040519081016040528092919081815260200182805462000601906200250f565b8015620006525780601f10620006265761010080835404028352916020019162000652565b820191906000526020600020905b8154815290600101906020018083116200063457829003601f168201915b5050505050905090565b3360009081526068602052604090205460ff16620006975760405162461bcd60e51b81526004016200068e90620023f7565b60405180910390fd5b5050565b60008033898989898989604051602401620006bd979695949392919062002375565b60408051601f198184030181529190526020810180516001600160e01b0316636eadde4360e01b1790529050620006f48162000701565b9998505050505050505050565b60755460009081906200071a90849061ffff1662000721565b9392505050565b6071546000906001600160a01b03166200074f5760405162461bcd60e51b81526004016200068e906200241b565b61ffff82166000908152607460205260409020546001600160a01b031680620007b35760405162461bcd60e51b81526020600482015260156024820152744d495353494e475f4c4f434b5f54454d504c41544560581b60448201526064016200068e565b60715460405160009183916001600160a01b03909116908790620007d79062001d03565b620007e593929190620022a1565b604051809103906000f08015801562000802573d6000803e3d6000fd5b50604080516060810182526001808252600060208084018281528486018381526001600160a01b03881680855260689093528684209551865460ff191690151517865590519385019390935591516002909301929092559151929350839233917f01017ed19df0c7f8acc436147b234b09664a9fb4797b4fa3fb9e599c2eb67be791a395945050505050565b6200089862000a72565b620008b75760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b038281166000908152606c6020526040902080546001600160a01b0319169183169182179055156200069757606d5460405163c640752d60e01b81526001600160a01b03848116600483015291821660248201529082169063c640752d90604401600060405180830381600087803b1580156200093a57600080fd5b505af11580156200094f573d6000803e3d6000fd5b505050505050565b6200096162000a72565b620009805760405162461bcd60e51b81526004016200068e9062002448565b6200098c600062001b51565b565b6200099862000a72565b620009b75760405162461bcd60e51b81526004016200068e9062002448565b606e80546001600160a01b03808b166001600160a01b031992831617909255606d8054928a1692909116919091179055606f869055620009fa606a868662001d11565b5062000a096069848462001d11565b5060708190556040517fa14230a1687e9bb8cdc0f3931d27c3a806e88c1c2235ba7756d6911feb35be679062000a4f908a908a908a908a908a908a908a908a90620022d8565b60405180910390a15050505050505050565b606060698054620005d3906200250f565b6033546001600160a01b0316331490565b3360009081526068602052604090205460ff1662000ab55760405162461bcd60e51b81526004016200068e90620023f7565b81156200069757600080336001600160a01b0316639d76ea586040518163ffffffff1660e01b815260040160206040518083038186803b15801562000af957600080fd5b505afa15801562000b0e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000b34919062001ebd565b90506001600160a01b0381161580159062000b5d5750606d546001600160a01b03828116911614155b1562000c1c576001600160a01b038082166000908152606c602052604090205416801562000c1557606d5460405163c1e553e760e01b81526001600160a01b038084169263c1e553e79262000bbc9287928b9291169060040162002339565b602060405180830381600087803b15801562000bd757600080fd5b505af115801562000bec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c12919062002115565b92505b5062000c20565b8391505b62000c2e8282863362001ba3565b336000908152606860205260408120600101805484929062000c5290849062002495565b90915550506001600160a01b03831615620012e857606e546001600160a01b039081166000908152606c6020526040902054168015620012e657606e54606d5460405163c1e553e760e01b81526000926001600160a01b038086169363c1e553e79362000cd193831692670de0b6b3a764000092169060040162002339565b602060405180830381600087803b15801562000cec57600080fd5b505af115801562000d01573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d27919062002115565b90506000306001600160a01b031663ba5ea0d56040518163ffffffff1660e01b815260040160206040518083038186803b15801562000d6557600080fd5b505afa92505050801562000d98575060408051601f3d908101601f1916820190925262000d959181019062002115565b60015b62000da65750606462000dbc565b8062000db6576064915062000dba565b8091505b505b600082606483606f5462000dd19190620024d3565b62000de6906806c6b935b8bbd40000620024d3565b62000df29190620024b0565b62000dfe9190620024b0565b905060006001607054111562000eec576066548062000e1f896002620024d3565b62000e2b9190620024b0565b62000e3890600262002495565b606e546040516370a0823160e01b81528a916001600160a01b0316906370a082319062000e6a9030906004016200228d565b602060405180830381600087803b15801562000e8557600080fd5b505af115801562000e9a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ec0919062002115565b62000ecc9190620024d3565b62000ed89190620024b0565b62000ee49190620024b0565b905062000fa5565b606654600288606e60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801562000f4357600080fd5b505af115801562000f58573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000f7e919062002115565b62000f8a9190620024d3565b62000f969190620024b0565b62000fa29190620024b0565b90505b8082111562000fb2578091505b8115620012e1576000606462000fca846014620024d3565b62000fd69190620024b0565b905060016070541115620011af57606e546040516370a0823160e01b81526000916001600160a01b0316906370a0823190620010179030906004016200228d565b602060405180830381600087803b1580156200103257600080fd5b505af115801562001047573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200106d919062002115565b905083811115620011a857606e546001600160a01b031663a9059cbb8b620010968588620024f5565b6040518363ffffffff1660e01b8152600401620010b59291906200235c565b602060405180830381600087803b158015620010d057600080fd5b505af1158015620010e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200110b91906200202d565b50606e546001600160a01b031663a9059cbb620011306033546001600160a01b031690565b846040518363ffffffff1660e01b8152600401620011509291906200235c565b602060405180830381600087803b1580156200116b57600080fd5b505af115801562001180573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620011a691906200202d565b505b50620012df565b606e546001600160a01b03166340c10f198a620011cd8487620024f5565b6040518363ffffffff1660e01b8152600401620011ec9291906200235c565b602060405180830381600087803b1580156200120757600080fd5b505af11580156200121c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200124291906200202d565b50606e546001600160a01b03166340c10f19620012676033546001600160a01b031690565b836040518363ffffffff1660e01b8152600401620012879291906200235c565b602060405180830381600087803b158015620012a257600080fd5b505af1158015620012b7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620012dd91906200202d565b505b505b505050505b505b50505050565b620012f862000a72565b620013175760405162461bcd60e51b81526004016200068e9062002448565b6066829055606781905560408051838152602081018390527f7b2ce3c83b45f79993ff2cbf5651caff2dfe04010b4846e03066b84e3e4059bb910160405180910390a15050565b606980546200136d906200250f565b80601f01602080910402602001604051908101604052809291908181526020018280546200139b906200250f565b8015620013ec5780601f10620013c057610100808354040283529160200191620013ec565b820191906000526020600020905b815481529060010190602001808311620013ce57829003601f168201915b505050505081565b600054610100900460ff16620014115760005460ff161562001415565b303b155b620014345760405162461bcd60e51b81526004016200068e90620023ca565b600054610100900460ff1615801562001457576000805461ffff19166101011790555b603380546001600160a01b0319166001600160a01b0384169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3801562000697576000805461ff00191690555050565b600054610100900460ff16620014d35760005460ff1615620014d7565b303b155b620014f65760405162461bcd60e51b81526004016200068e90620023ca565b600054610100900460ff1615801562001519576000805461ffff19166101011790555b6200152482620013f4565b6200152e62001c14565b50801562000697576000805461ff00191690555050565b6200154f62000a72565b6200156e5760405162461bcd60e51b81526004016200068e9062002448565b6071546001600160a01b031615620015bc5760405162461bcd60e51b815260206004820152601060248201526f1053149150511657d111541313d6515160821b60448201526064016200068e565b620015c662001c14565b50565b620015d362000a72565b620015f25760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b0382166000818152607360209081526040808320805461ffff191661ffff8781169182179092558085526074909352922080546001600160a01b03191690931790925560755416101562001659576075805461ffff191661ffff83161790555b60405161ffff8216906001600160a01b038416907fd36d021c4aa145e30eadc73e34284101030d688b9eb9413a4dc3af40ab1d474890600090a35050565b6071546000906001600160a01b0316620016c55760405162461bcd60e51b81526004016200068e906200241b565b620016d1833362001c74565b1515600114620017135760405162461bcd60e51b815260206004820152600c60248201526b4d414e414745525f4f4e4c5960a01b60448201526064016200068e565b60008390506000816001600160a01b031663d1bbd49c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200175457600080fd5b505afa15801562001769573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200178f9190620020f5565b90506200179e8160016200246c565b61ffff168461ffff1614620017e95760405162461bcd60e51b815260206004820152601060248201526f0ac8aa4a6929e9cbea89e9ebe90928e960831b60448201526064016200068e565b61ffff84166000908152607460205260409020546001600160a01b031680620018485760405162461bcd60e51b815260206004820152601060248201526f4d495353494e475f54454d504c41544560801b60448201526064016200068e565b60725460405163266a23b160e21b81526001600160a01b0380891660048301528381166024830152889216906399a88ec490604401600060405180830381600087803b1580156200189857600080fd5b505af1158015620018ad573d6000803e3d6000fd5b5050604051638932a90d60e01b8152602060048201526002602482015261060f60f31b60448201526001600160a01b0387169250638932a90d9150606401600060405180830381600087803b1580156200190657600080fd5b505af11580156200191b573d6000803e3d6000fd5b5050604080516001600160a01b038b16815261ffff8a1660208201527f5e7b70a9e6f0b4c7cf2657cf563ce85eed3aebb7731396118d01a195d218fd5e935001905060405180910390a1509495945050505050565b606a80546200136d906200250f565b6200198962000a72565b620019a85760405162461bcd60e51b81526004016200068e9062002448565b604051636eadde4360e01b815230600482015260006024820181905260448201819052606482018190526084820181905260c060a483015260c48201526001600160a01b03821690636eadde439060e401600060405180830381600087803b15801562001a1457600080fd5b505af115801562001a29573d6000803e3d6000fd5b50505050806001600160a01b031663f0ba60406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562001a6957600080fd5b505af115801562001a7e573d6000803e3d6000fd5b5050606b80546001600160a01b0319166001600160a01b03851617905550506040517f4fa58418e580e78539e8d3d8d17d94dbaca0927533953c8248f313e6f8927c219062001acf9083906200228d565b60405180910390a150565b62001ae462000a72565b62001b035760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b03811662001b4b5760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa7aba722a960991b60448201526064016200068e565b620015c6815b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b8360665462001bb3919062002495565b606681905560408051918252602082018690526001600160a01b03858116838301526060830185905283166080830152517f3b50eb9d9b4a8db204f2928c9e572c2865b0d02803493ccb6aa256848323ebb79181900360a00190a150505050565b600060405162001c249062001da0565b604051809103906000f08015801562001c41573d6000803e3d6000fd5b50607280546001600160a01b03929092166001600160a01b03199283168117909155607180549092168117909155919050565b60405163aae4b8f760e01b815260009083906001600160a01b0382169063aae4b8f79062001ca79086906004016200228d565b60206040518083038186803b15801562001cc057600080fd5b505afa15801562001cd5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cfb91906200202d565b949350505050565b610f3980620025a083390190565b82805462001d1f906200250f565b90600052602060002090601f01602090048101928262001d43576000855562001d8e565b82601f1062001d5e5782800160ff1982351617855562001d8e565b8280016001018555821562001d8e579182015b8281111562001d8e57823582559160200191906001019062001d71565b5062001d9c92915062001dae565b5090565b6107be80620034d983390190565b5b8082111562001d9c576000815560010162001daf565b600082601f83011262001dd757600080fd5b81356001600160401b038082111562001df45762001df462002562565b604051601f8301601f19908116603f0116810190828211818310171562001e1f5762001e1f62002562565b8160405283815286602085880101111562001e3957600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008083601f84011262001e6c57600080fd5b5081356001600160401b0381111562001e8457600080fd5b602083019150836020828501011115620005bb57600080fd5b60006020828403121562001eb057600080fd5b81356200071a8162002578565b60006020828403121562001ed057600080fd5b81516200071a8162002578565b6000806040838503121562001ef157600080fd5b823562001efe8162002578565b9150602083013562001f10816200258e565b809150509250929050565b6000806040838503121562001f2f57600080fd5b823562001f3c8162002578565b9150602083013562001f108162002578565b60008060008060008060008060c0898b03121562001f6b57600080fd5b883562001f788162002578565b9750602089013562001f8a8162002578565b96506040890135955060608901356001600160401b038082111562001fae57600080fd5b62001fbc8c838d0162001e59565b909750955060808b013591508082111562001fd657600080fd5b5062001fe58b828c0162001e59565b999c989b50969995989497949560a00135949350505050565b600080604083850312156200201257600080fd5b82356200201f8162002578565b946020939093013593505050565b6000602082840312156200204057600080fd5b815180151581146200071a57600080fd5b6000602082840312156200206457600080fd5b81356001600160401b038111156200207b57600080fd5b62001cfb8482850162001dc5565b600080604083850312156200209d57600080fd5b82356001600160401b03811115620020b457600080fd5b620020c28582860162001dc5565b925050602083013562001f10816200258e565b600060208284031215620020e857600080fd5b81356200071a816200258e565b6000602082840312156200210857600080fd5b81516200071a816200258e565b6000602082840312156200212857600080fd5b5051919050565b600080604083850312156200214357600080fd5b82359150602083013562001f108162002578565b600080600080600080600060c0888a0312156200217357600080fd5b873596506020880135620021878162002578565b9550604088013594506060880135935060808801356001600160401b03811115620021b157600080fd5b620021bf8a828b0162001e59565b90945092505060a08801356001600160a01b031981168114620021e157600080fd5b8091505092959891949750929550565b600080604083850312156200220557600080fd5b50508035926020909101359150565b6000815180845260005b818110156200223c576020818501810151868301820152016200221e565b818111156200224f576000602083870101525b50601f01601f19169290920160200192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0391909116815260200190565b6001600160a01b03848116825283166020820152606060408201819052600090620022cf9083018462002214565b95945050505050565b6001600160a01b038981168252881660208201526040810187905260c0606082018190526000906200230e908301878962002264565b82810360808401526200232381868862002264565b9150508260a08301529998505050505050505050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03929092168252602082015260400190565b600060018060a01b03808a16835288602084015280881660408401525085606083015284608083015260c060a0830152620006f460c08301848662002264565b6020815260006200071a602083018462002214565b6020808252601390820152721053149150511657d253925512505312569151606a1b604082015260600190565b6020808252600a90820152694f4e4c595f4c4f434b5360b01b604082015260600190565b60208082526013908201527226a4a9a9a4a723afa82927ac2cafa0a226a4a760691b604082015260600190565b6020808252600a908201526927a7262cafa7aba722a960b11b604082015260600190565b600061ffff8083168185168083038211156200248c576200248c6200254c565b01949350505050565b60008219821115620024ab57620024ab6200254c565b500190565b600082620024ce57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615620024f057620024f06200254c565b500290565b6000828210156200250a576200250a6200254c565b500390565b600181811c908216806200252457607f821691505b602082108114156200254657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114620015c657600080fd5b61ffff81168114620015c657600080fdfe608060405260405162000f3938038062000f398339810160408190526200002691620004c2565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd620005f5565b60008051602062000ef2833981519152146200007557620000756200064a565b6200008382826000620000e7565b50620000b3905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6104620005f5565b60008051602062000ed283398151915214620000d357620000d36200064a565b620000de8262000124565b50505062000676565b620000f2836200017f565b600082511180620001005750805b156200011f576200011d8383620001c160201b620002601760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200014f620001f0565b604080516001600160a01b03928316815291841660208301520160405180910390a16200017c8162000229565b50565b6200018a81620002de565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6060620001e9838360405180606001604052806027815260200162000f126027913962000381565b9392505050565b60006200021a60008051602062000ed283398151915260001b6200045e60201b620002081760201c565b546001600160a01b0316919050565b6001600160a01b038116620002945760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b80620002bd60008051602062000ed283398151915260001b6200045e60201b620002081760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b620002f4816200046160201b6200028c1760201c565b620003585760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016200028b565b80620002bd60008051602062000ef283398151915260001b6200045e60201b620002081760201c565b6060833b620003e25760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084016200028b565b600080856001600160a01b031685604051620003ff9190620005a2565b600060405180830381855af49150503d80600081146200043c576040519150601f19603f3d011682016040523d82523d6000602084013e62000441565b606091505b5090925090506200045482828662000467565b9695505050505050565b90565b3b151590565b6060831562000478575081620001e9565b825115620004895782518084602001fd5b8160405162461bcd60e51b81526004016200028b9190620005c0565b80516001600160a01b0381168114620004bd57600080fd5b919050565b600080600060608486031215620004d857600080fd5b620004e384620004a5565b9250620004f360208501620004a5565b60408501519092506001600160401b03808211156200051157600080fd5b818601915086601f8301126200052657600080fd5b8151818111156200053b576200053b62000660565b604051601f8201601f19908116603f0116810190838211818310171562000566576200056662000660565b816040528281528960208487010111156200058057600080fd5b620005938360208301602088016200061b565b80955050505050509250925092565b60008251620005b68184602087016200061b565b9190910192915050565b6020815260008251806020840152620005e18160408501602087016200061b565b601f01601f19169190910160400192915050565b6000828210156200061657634e487b7160e01b600052601160045260246000fd5b500390565b60005b83811015620006385781810151838201526020016200061e565b838111156200011d5750506000910152565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b61084c80620006866000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106d6565b610118565b61005b6100933660046106f1565b61015f565b3480156100a457600080fd5b506100ad6101d0565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106d6565b61020b565b3480156100f557600080fd5b506100ad610235565b610106610292565b610116610111610331565b61033b565b565b61012061035f565b6001600160a01b0316336001600160a01b031614156101575761015481604051806020016040528060008152506000610392565b50565b6101546100fe565b61016761035f565b6001600160a01b0316336001600160a01b031614156101c8576101c38383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610392915050565b505050565b6101c36100fe565b60006101da61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb610331565b905090565b6102086100fe565b90565b61021361035f565b6001600160a01b0316336001600160a01b0316141561015757610154816103bd565b600061023f61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb61035f565b606061028583836040518060600160405280602781526020016107f060279139610411565b9392505050565b3b151590565b61029a61035f565b6001600160a01b0316336001600160a01b031614156101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fb6104e5565b3660008037600080366000845af43d6000803e80801561035a573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61039b8361050d565b6000825111806103a85750805b156101c3576103b78383610260565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e661035f565b604080516001600160a01b03928316815291841660208301520160405180910390a16101548161054d565b6060833b6104705760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610328565b600080856001600160a01b03168560405161048b9190610774565b600060405180830381855af49150503d80600081146104c6576040519150601f19603f3d011682016040523d82523d6000602084013e6104cb565b606091505b50915091506104db8282866105f6565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610383565b6105168161062f565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105b25760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610328565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60608315610605575081610285565b8251156106155782518084602001fd5b8160405162461bcd60e51b81526004016103289190610790565b803b6106935760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610328565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105d5565b80356001600160a01b03811681146106d157600080fd5b919050565b6000602082840312156106e857600080fd5b610285826106ba565b60008060006040848603121561070657600080fd5b61070f846106ba565b9250602084013567ffffffffffffffff8082111561072c57600080fd5b818601915086601f83011261074057600080fd5b81358181111561074f57600080fd5b87602082850101111561076157600080fd5b6020830194508093505050509250925092565b600082516107868184602087016107c3565b9190910192915050565b60208152600082518060208401526107af8160408501602087016107c3565b601f01601f19169190910160400192915050565b60005b838110156107de5781810151838201526020016107c6565b838111156103b7575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069013180797a320073b51923c269b12cc1f6ad3ad59099b708d6ca452b6c416864736f6c63430008070033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6107408061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100f257806399a88ec414610105578063f2fde38b14610125578063f3b7dead14610145575b600080fd5b34801561007c57600080fd5b5061009061008b3660046104e1565b610165565b60405161009d9190610631565b60405180910390f35b3480156100b257600080fd5b506100bb6101f6565b005b3480156100c957600080fd5b506100bb6100d8366004610522565b61023a565b3480156100e957600080fd5b506100906102cb565b6100bb61010036600461055b565b6102da565b34801561011157600080fd5b506100bb610120366004610522565b610370565b34801561013157600080fd5b506100bb6101403660046104e1565b6103cb565b34801561015157600080fd5b506100906101603660046104e1565b61046b565b6000806000836001600160a01b031660405161018b90635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101c6576040519150601f19603f3d011682016040523d82523d6000602084013e6101cb565b606091505b5091509150816101da57600080fd5b808060200190518101906101ee9190610505565b949350505050565b336101ff6102cb565b6001600160a01b03161461022e5760405162461bcd60e51b8152600401610225906106aa565b60405180910390fd5b6102386000610491565b565b336102436102cb565b6001600160a01b0316146102695760405162461bcd60e51b8152600401610225906106aa565b6040516308f2839760e41b81526001600160a01b03831690638f28397090610295908490600401610631565b600060405180830381600087803b1580156102af57600080fd5b505af11580156102c3573d6000803e3d6000fd5b505050505050565b6000546001600160a01b031690565b336102e36102cb565b6001600160a01b0316146103095760405162461bcd60e51b8152600401610225906106aa565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906103399086908690600401610645565b6000604051808303818588803b15801561035257600080fd5b505af1158015610366573d6000803e3d6000fd5b5050505050505050565b336103796102cb565b6001600160a01b03161461039f5760405162461bcd60e51b8152600401610225906106aa565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe690610295908490600401610631565b336103d46102cb565b6001600160a01b0316146103fa5760405162461bcd60e51b8152600401610225906106aa565b6001600160a01b03811661045f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610225565b61046881610491565b50565b6000806000836001600160a01b031660405161018b906303e1469160e61b815260040190565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156104f357600080fd5b81356104fe816106f5565b9392505050565b60006020828403121561051757600080fd5b81516104fe816106f5565b6000806040838503121561053557600080fd5b8235610540816106f5565b91506020830135610550816106f5565b809150509250929050565b60008060006060848603121561057057600080fd5b833561057b816106f5565b9250602084013561058b816106f5565b9150604084013567ffffffffffffffff808211156105a857600080fd5b818601915086601f8301126105bc57600080fd5b8135818111156105ce576105ce6106df565b604051601f8201601f19908116603f011681019083821181831017156105f6576105f66106df565b8160405282815289602084870101111561060f57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6001600160a01b0391909116815260200190565b60018060a01b038316815260006020604081840152835180604085015260005b8181101561068157858101830151858201606001528201610665565b81811115610693576000606083870101525b50601f01601f191692909201606001949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461046857600080fdfea2646970667358221220125c3f15bab352eefb1c849f6ce22a786402ca0e764b273ba139e89ca5d91fd764736f6c63430008070033a26469706673582212208276f85b113e4c6bf61a01f1e095ec615afd8bb29e629b7aac28d14a42b0636b64736f6c63430008070033

Deployed Bytecode

0x60806040523480156200001157600080fd5b50600436106200021d5760003560e01c80638da5cb5b1162000127578063c4d66de811620000b6578063c4d66de81462000503578063c7d7d0c9146200051a578063caeee3a31462000524578063ccf54648146200053b578063cd93b5321462000552578063cec410521462000561578063d6e9e803146200056b578063ef67fde61462000575578063f2fde38b146200058c578063f832899114620005a357600080fd5b80638da5cb5b146200046c5780638f32d59b146200047e578063939d9f1f1462000499578063963a947814620004b05780639a8a059214620004c7578063a082eb0e14620004d1578063a998e9fb14620004db578063b0ca0c8314620004e5578063ba5ea0d514620004fc57600080fd5b8063487a8a7611620001b0578063487a8a7614620003365780635979e755146200034d5780635c38eb3a14620003615780635de9a1371462000378578063666018a514620003cc5780636bced5a214620003fc578063715018a61462000428578063743bbc2f14620004325780637d8fb641146200044b5780637ff94bb2146200046257600080fd5b80630cb175e3146200022257806325772ad91462000253578063262d0a85146200029757806335a750de14620002ba5780633652466314620002d35780633fc8cef314620002ec5780634220bd46146200030057806342d9210614620003085780634452c2b2146200031f575b600080fd5b620002396200023336600462001ffe565b620005b7565b604080519283526020830191909152015b60405180910390f35b620002836200026436600462001e9d565b6001600160a01b031660009081526073602052604090205461ffff1690565b60405161ffff90911681526020016200024a565b606e54620002ab906001600160a01b031681565b6040516200024a91906200228d565b620002c4620005c2565b6040516200024a9190620023b5565b620002ea620002e4366004620021f1565b6200065c565b005b606d54620002ab906001600160a01b031681565b600b62000283565b620002ab6200031936600462002157565b6200069b565b620002ab6200033036600462002051565b62000701565b620002ab6200034736600462002089565b62000721565b607154620002ab906001600160a01b031681565b620002ea6200037236600462001f1b565b6200088e565b620003ae6200038936600462001e9d565b60686020526000908152604090208054600182015460029092015460ff909116919083565b6040805193151584526020840192909252908201526060016200024a565b620002ab620003dd366004620020d5565b61ffff166000908152607460205260409020546001600160a01b031690565b620002ab6200040d36600462001e9d565b606c602052600090815260409020546001600160a01b031681565b620002ea62000957565b6200043c606f5481565b6040519081526020016200024a565b620002ea6200045c36600462001f4e565b6200098e565b620002c462000a61565b6033546001600160a01b0316620002ab565b6200048862000a72565b60405190151581526020016200024a565b620002ea620004aa3660046200212f565b62000a83565b620002ea620004c1366004620021f1565b620012ee565b6200043c60705481565b6200043c60675481565b620002c46200135e565b620002ea620004f636600462001e9d565b620013f4565b486200043c565b620002ea6200051436600462001e9d565b620014b6565b620002ea62001545565b620002ea6200053536600462001edd565b620015c9565b620002ab6200054c36600462001edd565b62001697565b607554620002839061ffff1681565b620002c462001970565b6200043c60665481565b620002ea6200058636600462001e9d565b6200197f565b620002ea6200059d36600462001e9d565b62001ada565b606b54620002ab906001600160a01b031681565b6000805b9250929050565b6060606a8054620005d3906200250f565b80601f016020809104026020016040519081016040528092919081815260200182805462000601906200250f565b8015620006525780601f10620006265761010080835404028352916020019162000652565b820191906000526020600020905b8154815290600101906020018083116200063457829003601f168201915b5050505050905090565b3360009081526068602052604090205460ff16620006975760405162461bcd60e51b81526004016200068e90620023f7565b60405180910390fd5b5050565b60008033898989898989604051602401620006bd979695949392919062002375565b60408051601f198184030181529190526020810180516001600160e01b0316636eadde4360e01b1790529050620006f48162000701565b9998505050505050505050565b60755460009081906200071a90849061ffff1662000721565b9392505050565b6071546000906001600160a01b03166200074f5760405162461bcd60e51b81526004016200068e906200241b565b61ffff82166000908152607460205260409020546001600160a01b031680620007b35760405162461bcd60e51b81526020600482015260156024820152744d495353494e475f4c4f434b5f54454d504c41544560581b60448201526064016200068e565b60715460405160009183916001600160a01b03909116908790620007d79062001d03565b620007e593929190620022a1565b604051809103906000f08015801562000802573d6000803e3d6000fd5b50604080516060810182526001808252600060208084018281528486018381526001600160a01b03881680855260689093528684209551865460ff191690151517865590519385019390935591516002909301929092559151929350839233917f01017ed19df0c7f8acc436147b234b09664a9fb4797b4fa3fb9e599c2eb67be791a395945050505050565b6200089862000a72565b620008b75760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b038281166000908152606c6020526040902080546001600160a01b0319169183169182179055156200069757606d5460405163c640752d60e01b81526001600160a01b03848116600483015291821660248201529082169063c640752d90604401600060405180830381600087803b1580156200093a57600080fd5b505af11580156200094f573d6000803e3d6000fd5b505050505050565b6200096162000a72565b620009805760405162461bcd60e51b81526004016200068e9062002448565b6200098c600062001b51565b565b6200099862000a72565b620009b75760405162461bcd60e51b81526004016200068e9062002448565b606e80546001600160a01b03808b166001600160a01b031992831617909255606d8054928a1692909116919091179055606f869055620009fa606a868662001d11565b5062000a096069848462001d11565b5060708190556040517fa14230a1687e9bb8cdc0f3931d27c3a806e88c1c2235ba7756d6911feb35be679062000a4f908a908a908a908a908a908a908a908a90620022d8565b60405180910390a15050505050505050565b606060698054620005d3906200250f565b6033546001600160a01b0316331490565b3360009081526068602052604090205460ff1662000ab55760405162461bcd60e51b81526004016200068e90620023f7565b81156200069757600080336001600160a01b0316639d76ea586040518163ffffffff1660e01b815260040160206040518083038186803b15801562000af957600080fd5b505afa15801562000b0e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000b34919062001ebd565b90506001600160a01b0381161580159062000b5d5750606d546001600160a01b03828116911614155b1562000c1c576001600160a01b038082166000908152606c602052604090205416801562000c1557606d5460405163c1e553e760e01b81526001600160a01b038084169263c1e553e79262000bbc9287928b9291169060040162002339565b602060405180830381600087803b15801562000bd757600080fd5b505af115801562000bec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c12919062002115565b92505b5062000c20565b8391505b62000c2e8282863362001ba3565b336000908152606860205260408120600101805484929062000c5290849062002495565b90915550506001600160a01b03831615620012e857606e546001600160a01b039081166000908152606c6020526040902054168015620012e657606e54606d5460405163c1e553e760e01b81526000926001600160a01b038086169363c1e553e79362000cd193831692670de0b6b3a764000092169060040162002339565b602060405180830381600087803b15801562000cec57600080fd5b505af115801562000d01573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d27919062002115565b90506000306001600160a01b031663ba5ea0d56040518163ffffffff1660e01b815260040160206040518083038186803b15801562000d6557600080fd5b505afa92505050801562000d98575060408051601f3d908101601f1916820190925262000d959181019062002115565b60015b62000da65750606462000dbc565b8062000db6576064915062000dba565b8091505b505b600082606483606f5462000dd19190620024d3565b62000de6906806c6b935b8bbd40000620024d3565b62000df29190620024b0565b62000dfe9190620024b0565b905060006001607054111562000eec576066548062000e1f896002620024d3565b62000e2b9190620024b0565b62000e3890600262002495565b606e546040516370a0823160e01b81528a916001600160a01b0316906370a082319062000e6a9030906004016200228d565b602060405180830381600087803b15801562000e8557600080fd5b505af115801562000e9a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ec0919062002115565b62000ecc9190620024d3565b62000ed89190620024b0565b62000ee49190620024b0565b905062000fa5565b606654600288606e60009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801562000f4357600080fd5b505af115801562000f58573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000f7e919062002115565b62000f8a9190620024d3565b62000f969190620024b0565b62000fa29190620024b0565b90505b8082111562000fb2578091505b8115620012e1576000606462000fca846014620024d3565b62000fd69190620024b0565b905060016070541115620011af57606e546040516370a0823160e01b81526000916001600160a01b0316906370a0823190620010179030906004016200228d565b602060405180830381600087803b1580156200103257600080fd5b505af115801562001047573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200106d919062002115565b905083811115620011a857606e546001600160a01b031663a9059cbb8b620010968588620024f5565b6040518363ffffffff1660e01b8152600401620010b59291906200235c565b602060405180830381600087803b158015620010d057600080fd5b505af1158015620010e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200110b91906200202d565b50606e546001600160a01b031663a9059cbb620011306033546001600160a01b031690565b846040518363ffffffff1660e01b8152600401620011509291906200235c565b602060405180830381600087803b1580156200116b57600080fd5b505af115801562001180573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620011a691906200202d565b505b50620012df565b606e546001600160a01b03166340c10f198a620011cd8487620024f5565b6040518363ffffffff1660e01b8152600401620011ec9291906200235c565b602060405180830381600087803b1580156200120757600080fd5b505af11580156200121c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200124291906200202d565b50606e546001600160a01b03166340c10f19620012676033546001600160a01b031690565b836040518363ffffffff1660e01b8152600401620012879291906200235c565b602060405180830381600087803b158015620012a257600080fd5b505af1158015620012b7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620012dd91906200202d565b505b505b505050505b505b50505050565b620012f862000a72565b620013175760405162461bcd60e51b81526004016200068e9062002448565b6066829055606781905560408051838152602081018390527f7b2ce3c83b45f79993ff2cbf5651caff2dfe04010b4846e03066b84e3e4059bb910160405180910390a15050565b606980546200136d906200250f565b80601f01602080910402602001604051908101604052809291908181526020018280546200139b906200250f565b8015620013ec5780601f10620013c057610100808354040283529160200191620013ec565b820191906000526020600020905b815481529060010190602001808311620013ce57829003601f168201915b505050505081565b600054610100900460ff16620014115760005460ff161562001415565b303b155b620014345760405162461bcd60e51b81526004016200068e90620023ca565b600054610100900460ff1615801562001457576000805461ffff19166101011790555b603380546001600160a01b0319166001600160a01b0384169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3801562000697576000805461ff00191690555050565b600054610100900460ff16620014d35760005460ff1615620014d7565b303b155b620014f65760405162461bcd60e51b81526004016200068e90620023ca565b600054610100900460ff1615801562001519576000805461ffff19166101011790555b6200152482620013f4565b6200152e62001c14565b50801562000697576000805461ff00191690555050565b6200154f62000a72565b6200156e5760405162461bcd60e51b81526004016200068e9062002448565b6071546001600160a01b031615620015bc5760405162461bcd60e51b815260206004820152601060248201526f1053149150511657d111541313d6515160821b60448201526064016200068e565b620015c662001c14565b50565b620015d362000a72565b620015f25760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b0382166000818152607360209081526040808320805461ffff191661ffff8781169182179092558085526074909352922080546001600160a01b03191690931790925560755416101562001659576075805461ffff191661ffff83161790555b60405161ffff8216906001600160a01b038416907fd36d021c4aa145e30eadc73e34284101030d688b9eb9413a4dc3af40ab1d474890600090a35050565b6071546000906001600160a01b0316620016c55760405162461bcd60e51b81526004016200068e906200241b565b620016d1833362001c74565b1515600114620017135760405162461bcd60e51b815260206004820152600c60248201526b4d414e414745525f4f4e4c5960a01b60448201526064016200068e565b60008390506000816001600160a01b031663d1bbd49c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200175457600080fd5b505afa15801562001769573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200178f9190620020f5565b90506200179e8160016200246c565b61ffff168461ffff1614620017e95760405162461bcd60e51b815260206004820152601060248201526f0ac8aa4a6929e9cbea89e9ebe90928e960831b60448201526064016200068e565b61ffff84166000908152607460205260409020546001600160a01b031680620018485760405162461bcd60e51b815260206004820152601060248201526f4d495353494e475f54454d504c41544560801b60448201526064016200068e565b60725460405163266a23b160e21b81526001600160a01b0380891660048301528381166024830152889216906399a88ec490604401600060405180830381600087803b1580156200189857600080fd5b505af1158015620018ad573d6000803e3d6000fd5b5050604051638932a90d60e01b8152602060048201526002602482015261060f60f31b60448201526001600160a01b0387169250638932a90d9150606401600060405180830381600087803b1580156200190657600080fd5b505af11580156200191b573d6000803e3d6000fd5b5050604080516001600160a01b038b16815261ffff8a1660208201527f5e7b70a9e6f0b4c7cf2657cf563ce85eed3aebb7731396118d01a195d218fd5e935001905060405180910390a1509495945050505050565b606a80546200136d906200250f565b6200198962000a72565b620019a85760405162461bcd60e51b81526004016200068e9062002448565b604051636eadde4360e01b815230600482015260006024820181905260448201819052606482018190526084820181905260c060a483015260c48201526001600160a01b03821690636eadde439060e401600060405180830381600087803b15801562001a1457600080fd5b505af115801562001a29573d6000803e3d6000fd5b50505050806001600160a01b031663f0ba60406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562001a6957600080fd5b505af115801562001a7e573d6000803e3d6000fd5b5050606b80546001600160a01b0319166001600160a01b03851617905550506040517f4fa58418e580e78539e8d3d8d17d94dbaca0927533953c8248f313e6f8927c219062001acf9083906200228d565b60405180910390a150565b62001ae462000a72565b62001b035760405162461bcd60e51b81526004016200068e9062002448565b6001600160a01b03811662001b4b5760405162461bcd60e51b815260206004820152600d60248201526c24a72b20a624a22fa7aba722a960991b60448201526064016200068e565b620015c6815b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b8360665462001bb3919062002495565b606681905560408051918252602082018690526001600160a01b03858116838301526060830185905283166080830152517f3b50eb9d9b4a8db204f2928c9e572c2865b0d02803493ccb6aa256848323ebb79181900360a00190a150505050565b600060405162001c249062001da0565b604051809103906000f08015801562001c41573d6000803e3d6000fd5b50607280546001600160a01b03929092166001600160a01b03199283168117909155607180549092168117909155919050565b60405163aae4b8f760e01b815260009083906001600160a01b0382169063aae4b8f79062001ca79086906004016200228d565b60206040518083038186803b15801562001cc057600080fd5b505afa15801562001cd5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001cfb91906200202d565b949350505050565b610f3980620025a083390190565b82805462001d1f906200250f565b90600052602060002090601f01602090048101928262001d43576000855562001d8e565b82601f1062001d5e5782800160ff1982351617855562001d8e565b8280016001018555821562001d8e579182015b8281111562001d8e57823582559160200191906001019062001d71565b5062001d9c92915062001dae565b5090565b6107be80620034d983390190565b5b8082111562001d9c576000815560010162001daf565b600082601f83011262001dd757600080fd5b81356001600160401b038082111562001df45762001df462002562565b604051601f8301601f19908116603f0116810190828211818310171562001e1f5762001e1f62002562565b8160405283815286602085880101111562001e3957600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008083601f84011262001e6c57600080fd5b5081356001600160401b0381111562001e8457600080fd5b602083019150836020828501011115620005bb57600080fd5b60006020828403121562001eb057600080fd5b81356200071a8162002578565b60006020828403121562001ed057600080fd5b81516200071a8162002578565b6000806040838503121562001ef157600080fd5b823562001efe8162002578565b9150602083013562001f10816200258e565b809150509250929050565b6000806040838503121562001f2f57600080fd5b823562001f3c8162002578565b9150602083013562001f108162002578565b60008060008060008060008060c0898b03121562001f6b57600080fd5b883562001f788162002578565b9750602089013562001f8a8162002578565b96506040890135955060608901356001600160401b038082111562001fae57600080fd5b62001fbc8c838d0162001e59565b909750955060808b013591508082111562001fd657600080fd5b5062001fe58b828c0162001e59565b999c989b50969995989497949560a00135949350505050565b600080604083850312156200201257600080fd5b82356200201f8162002578565b946020939093013593505050565b6000602082840312156200204057600080fd5b815180151581146200071a57600080fd5b6000602082840312156200206457600080fd5b81356001600160401b038111156200207b57600080fd5b62001cfb8482850162001dc5565b600080604083850312156200209d57600080fd5b82356001600160401b03811115620020b457600080fd5b620020c28582860162001dc5565b925050602083013562001f10816200258e565b600060208284031215620020e857600080fd5b81356200071a816200258e565b6000602082840312156200210857600080fd5b81516200071a816200258e565b6000602082840312156200212857600080fd5b5051919050565b600080604083850312156200214357600080fd5b82359150602083013562001f108162002578565b600080600080600080600060c0888a0312156200217357600080fd5b873596506020880135620021878162002578565b9550604088013594506060880135935060808801356001600160401b03811115620021b157600080fd5b620021bf8a828b0162001e59565b90945092505060a08801356001600160a01b031981168114620021e157600080fd5b8091505092959891949750929550565b600080604083850312156200220557600080fd5b50508035926020909101359150565b6000815180845260005b818110156200223c576020818501810151868301820152016200221e565b818111156200224f576000602083870101525b50601f01601f19169290920160200192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0391909116815260200190565b6001600160a01b03848116825283166020820152606060408201819052600090620022cf9083018462002214565b95945050505050565b6001600160a01b038981168252881660208201526040810187905260c0606082018190526000906200230e908301878962002264565b82810360808401526200232381868862002264565b9150508260a08301529998505050505050505050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b03929092168252602082015260400190565b600060018060a01b03808a16835288602084015280881660408401525085606083015284608083015260c060a0830152620006f460c08301848662002264565b6020815260006200071a602083018462002214565b6020808252601390820152721053149150511657d253925512505312569151606a1b604082015260600190565b6020808252600a90820152694f4e4c595f4c4f434b5360b01b604082015260600190565b60208082526013908201527226a4a9a9a4a723afa82927ac2cafa0a226a4a760691b604082015260600190565b6020808252600a908201526927a7262cafa7aba722a960b11b604082015260600190565b600061ffff8083168185168083038211156200248c576200248c6200254c565b01949350505050565b60008219821115620024ab57620024ab6200254c565b500190565b600082620024ce57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615620024f057620024f06200254c565b500290565b6000828210156200250a576200250a6200254c565b500390565b600181811c908216806200252457607f821691505b602082108114156200254657634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114620015c657600080fd5b61ffff81168114620015c657600080fdfe608060405260405162000f3938038062000f398339810160408190526200002691620004c2565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd620005f5565b60008051602062000ef2833981519152146200007557620000756200064a565b6200008382826000620000e7565b50620000b3905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6104620005f5565b60008051602062000ed283398151915214620000d357620000d36200064a565b620000de8262000124565b50505062000676565b620000f2836200017f565b600082511180620001005750805b156200011f576200011d8383620001c160201b620002601760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200014f620001f0565b604080516001600160a01b03928316815291841660208301520160405180910390a16200017c8162000229565b50565b6200018a81620002de565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6060620001e9838360405180606001604052806027815260200162000f126027913962000381565b9392505050565b60006200021a60008051602062000ed283398151915260001b6200045e60201b620002081760201c565b546001600160a01b0316919050565b6001600160a01b038116620002945760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b80620002bd60008051602062000ed283398151915260001b6200045e60201b620002081760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b620002f4816200046160201b6200028c1760201c565b620003585760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016200028b565b80620002bd60008051602062000ef283398151915260001b6200045e60201b620002081760201c565b6060833b620003e25760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084016200028b565b600080856001600160a01b031685604051620003ff9190620005a2565b600060405180830381855af49150503d80600081146200043c576040519150601f19603f3d011682016040523d82523d6000602084013e62000441565b606091505b5090925090506200045482828662000467565b9695505050505050565b90565b3b151590565b6060831562000478575081620001e9565b825115620004895782518084602001fd5b8160405162461bcd60e51b81526004016200028b9190620005c0565b80516001600160a01b0381168114620004bd57600080fd5b919050565b600080600060608486031215620004d857600080fd5b620004e384620004a5565b9250620004f360208501620004a5565b60408501519092506001600160401b03808211156200051157600080fd5b818601915086601f8301126200052657600080fd5b8151818111156200053b576200053b62000660565b604051601f8201601f19908116603f0116810190838211818310171562000566576200056662000660565b816040528281528960208487010111156200058057600080fd5b620005938360208301602088016200061b565b80955050505050509250925092565b60008251620005b68184602087016200061b565b9190910192915050565b6020815260008251806020840152620005e18160408501602087016200061b565b601f01601f19169190910160400192915050565b6000828210156200061657634e487b7160e01b600052601160045260246000fd5b500390565b60005b83811015620006385781810151838201526020016200061e565b838111156200011d5750506000910152565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b61084c80620006866000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106d6565b610118565b61005b6100933660046106f1565b61015f565b3480156100a457600080fd5b506100ad6101d0565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106d6565b61020b565b3480156100f557600080fd5b506100ad610235565b610106610292565b610116610111610331565b61033b565b565b61012061035f565b6001600160a01b0316336001600160a01b031614156101575761015481604051806020016040528060008152506000610392565b50565b6101546100fe565b61016761035f565b6001600160a01b0316336001600160a01b031614156101c8576101c38383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610392915050565b505050565b6101c36100fe565b60006101da61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb610331565b905090565b6102086100fe565b90565b61021361035f565b6001600160a01b0316336001600160a01b0316141561015757610154816103bd565b600061023f61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb61035f565b606061028583836040518060600160405280602781526020016107f060279139610411565b9392505050565b3b151590565b61029a61035f565b6001600160a01b0316336001600160a01b031614156101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fb6104e5565b3660008037600080366000845af43d6000803e80801561035a573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61039b8361050d565b6000825111806103a85750805b156101c3576103b78383610260565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e661035f565b604080516001600160a01b03928316815291841660208301520160405180910390a16101548161054d565b6060833b6104705760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610328565b600080856001600160a01b03168560405161048b9190610774565b600060405180830381855af49150503d80600081146104c6576040519150601f19603f3d011682016040523d82523d6000602084013e6104cb565b606091505b50915091506104db8282866105f6565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610383565b6105168161062f565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105b25760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610328565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60608315610605575081610285565b8251156106155782518084602001fd5b8160405162461bcd60e51b81526004016103289190610790565b803b6106935760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610328565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105d5565b80356001600160a01b03811681146106d157600080fd5b919050565b6000602082840312156106e857600080fd5b610285826106ba565b60008060006040848603121561070657600080fd5b61070f846106ba565b9250602084013567ffffffffffffffff8082111561072c57600080fd5b818601915086601f83011261074057600080fd5b81358181111561074f57600080fd5b87602082850101111561076157600080fd5b6020830194508093505050509250925092565b600082516107868184602087016107c3565b9190910192915050565b60208152600082518060208401526107af8160408501602087016107c3565b601f01601f19169190910160400192915050565b60005b838110156107de5781810151838201526020016107c6565b838111156103b7575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122069013180797a320073b51923c269b12cc1f6ad3ad59099b708d6ca452b6c416864736f6c63430008070033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6107408061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100f257806399a88ec414610105578063f2fde38b14610125578063f3b7dead14610145575b600080fd5b34801561007c57600080fd5b5061009061008b3660046104e1565b610165565b60405161009d9190610631565b60405180910390f35b3480156100b257600080fd5b506100bb6101f6565b005b3480156100c957600080fd5b506100bb6100d8366004610522565b61023a565b3480156100e957600080fd5b506100906102cb565b6100bb61010036600461055b565b6102da565b34801561011157600080fd5b506100bb610120366004610522565b610370565b34801561013157600080fd5b506100bb6101403660046104e1565b6103cb565b34801561015157600080fd5b506100906101603660046104e1565b61046b565b6000806000836001600160a01b031660405161018b90635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101c6576040519150601f19603f3d011682016040523d82523d6000602084013e6101cb565b606091505b5091509150816101da57600080fd5b808060200190518101906101ee9190610505565b949350505050565b336101ff6102cb565b6001600160a01b03161461022e5760405162461bcd60e51b8152600401610225906106aa565b60405180910390fd5b6102386000610491565b565b336102436102cb565b6001600160a01b0316146102695760405162461bcd60e51b8152600401610225906106aa565b6040516308f2839760e41b81526001600160a01b03831690638f28397090610295908490600401610631565b600060405180830381600087803b1580156102af57600080fd5b505af11580156102c3573d6000803e3d6000fd5b505050505050565b6000546001600160a01b031690565b336102e36102cb565b6001600160a01b0316146103095760405162461bcd60e51b8152600401610225906106aa565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906103399086908690600401610645565b6000604051808303818588803b15801561035257600080fd5b505af1158015610366573d6000803e3d6000fd5b5050505050505050565b336103796102cb565b6001600160a01b03161461039f5760405162461bcd60e51b8152600401610225906106aa565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe690610295908490600401610631565b336103d46102cb565b6001600160a01b0316146103fa5760405162461bcd60e51b8152600401610225906106aa565b6001600160a01b03811661045f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610225565b61046881610491565b50565b6000806000836001600160a01b031660405161018b906303e1469160e61b815260040190565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156104f357600080fd5b81356104fe816106f5565b9392505050565b60006020828403121561051757600080fd5b81516104fe816106f5565b6000806040838503121561053557600080fd5b8235610540816106f5565b91506020830135610550816106f5565b809150509250929050565b60008060006060848603121561057057600080fd5b833561057b816106f5565b9250602084013561058b816106f5565b9150604084013567ffffffffffffffff808211156105a857600080fd5b818601915086601f8301126105bc57600080fd5b8135818111156105ce576105ce6106df565b604051601f8201601f19908116603f011681019083821181831017156105f6576105f66106df565b8160405282815289602084870101111561060f57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6001600160a01b0391909116815260200190565b60018060a01b038316815260006020604081840152835180604085015260005b8181101561068157858101830151858201606001528201610665565b81811115610693576000606083870101525b50601f01601f191692909201606001949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461046857600080fdfea2646970667358221220125c3f15bab352eefb1c849f6ce22a786402ca0e764b273ba139e89ca5d91fd764736f6c63430008070033a26469706673582212208276f85b113e4c6bf61a01f1e095ec615afd8bb29e629b7aac28d14a42b0636b64736f6c63430008070033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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