Contract 0x80293ccaABaB3d3b9A549fAb1C433580a97A530B

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x0514017850da35cfde761eb0800384df9f39b1e4649afb6a00ba2d561fe51ba10x60806040204283782022-08-16 18:00:3444 days 22 hrs ago0xbbb0bb7ec7d45d1b22e6ec83ad329621fa468375 IN  Create: OKLGFaaS0 ETH0.011213853884 ETH
[ Download CSV Export 
Latest 2 internal transactions
Parent Txn Hash Block From To Value
0x0514017850da35cfde761eb0800384df9f39b1e4649afb6a00ba2d561fe51ba1204283782022-08-16 18:00:3444 days 22 hrs ago 0x80293ccaabab3d3b9a549fab1c433580a97a530b 0xa51ab19c9fd2af429eedf1cede008f65c1c0cdb30 ETH
0x0514017850da35cfde761eb0800384df9f39b1e4649afb6a00ba2d561fe51ba1204283782022-08-16 18:00:3444 days 22 hrs ago 0x80293ccaabab3d3b9a549fab1c433580a97a530b  Contract Creation0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OKLGFaaS

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 18 : OKLGFaaS.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import './OKLGFaaSTimePricing.sol';
import './OKLGFaaSToken.sol';
import './OKLGProduct.sol';

/**
 * @title OKLGFaaS (sOKLG)
 * @author Lance Whatley
 * @notice Implements the master FaaS contract to keep track of all tokens being added
 * to be staked and staking.
 */
contract OKLGFaaS is OKLGProduct {
  // this is a mapping of tokenAddress => contractAddress[] that represents
  // a particular address for the token that someone has put up
  // to be staked and a list of contract addresses for the staking token
  // contracts paying out stakers for the given token.
  mapping(address => address[]) public tokensUpForStaking;
  address[] public allFarmingContracts;
  uint256 public totalStakingContracts;

  OKLGFaaSTimePricing internal _faasPricing;

  /**
   * @notice The constructor for the staking master contract.
   */
  constructor(
    address _tokenAddress,
    address _spendAddress,
    address _linkPriceFeedContract,
    uint256 _blocksPerDay
  ) OKLGProduct(uint8(8), _tokenAddress, _spendAddress) {
    _faasPricing = new OKLGFaaSTimePricing(
      _linkPriceFeedContract,
      _blocksPerDay
    );
    _faasPricing.transferOwnership(msg.sender);
  }

  function getAllFarmingContracts() external view returns (address[] memory) {
    return allFarmingContracts;
  }

  function getTokensForStaking(address _tokenAddress)
    external
    view
    returns (address[] memory)
  {
    return tokensUpForStaking[_tokenAddress];
  }

  function createNewTokenContract(
    address _rewardsTokenAddy,
    address _stakedTokenAddy,
    uint256 _supply,
    uint256 _perBlockAllocation,
    uint256 _lockedUntilDate,
    uint256 _timelockSeconds,
    bool _isStakedNft
  ) external payable {
    _faasPricing.payForPool{ value: msg.value }(_supply, _perBlockAllocation);

    // create new OKLGFaaSToken contract which will serve as the core place for
    // users to stake their tokens and earn rewards
    ERC20 _rewToken = ERC20(_rewardsTokenAddy);

    // Send the new contract all the tokens from the sending user to be staked and harvested
    _rewToken.transferFrom(msg.sender, address(this), _supply);

    // in order to handle tokens that take tax, are burned, etc. when transferring, need to get
    // the user's balance after transferring in order to send the remainder of the tokens
    // instead of the full original supply. Similar to slippage on a DEX
    uint256 _updatedSupply = _supply <= _rewToken.balanceOf(address(this))
      ? _supply
      : _rewToken.balanceOf(address(this));

    OKLGFaaSToken _contract = new OKLGFaaSToken(
      'OKLG Staking Token',
      'sOKLG',
      _updatedSupply,
      _rewardsTokenAddy,
      _stakedTokenAddy,
      msg.sender,
      _perBlockAllocation,
      _lockedUntilDate,
      _timelockSeconds,
      _isStakedNft,
      address(_faasPricing)
    );
    allFarmingContracts.push(address(_contract));
    tokensUpForStaking[_stakedTokenAddy].push(address(_contract));
    totalStakingContracts++;

    _rewToken.transfer(address(_contract), _updatedSupply);

    // do one more double check on balance of rewards token
    // in the staking contract and update if need be
    uint256 _finalSupply = _updatedSupply <=
      _rewToken.balanceOf(address(_contract))
      ? _updatedSupply
      : _rewToken.balanceOf(address(_contract));
    if (_updatedSupply != _finalSupply) {
      _contract.updateSupply(_finalSupply);
    }
  }

  function removeTokenContract(address _faasTokenAddy) external {
    OKLGFaaSToken _contract = OKLGFaaSToken(_faasTokenAddy);
    require(
      msg.sender == _contract.tokenOwner(),
      'user must be the original token owner to remove tokens'
    );
    require(
      block.timestamp > _contract.getLockedUntilDate() &&
        _contract.getLockedUntilDate() != 0,
      'it must be after the locked time the user originally configured and not locked forever'
    );

    _contract.removeStakeableTokens();
    totalStakingContracts--;
  }

  function getFaasPricing() external view returns (address) {
    return address(_faasPricing);
  }
}

File 2 of 18 : OKLGFaaSTimePricing.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol';

contract OKLGFaaSTimePricing is Ownable {
  AggregatorV3Interface internal priceFeed;

  uint256 public timePeriodDays = 30; // don't convert to seconds because we calc against blocksPerDay below
  uint256 public priceUSDPerTimePeriod18 = 300 * 10**18;
  uint256 public blocksPerDay;

  constructor(address _linkPriceFeedContract, uint256 _blocksPerDay) {
    // https://docs.chain.link/docs/reference-contracts/
    // https://github.com/pcaversaccio/chainlink-price-feed/blob/main/README.md
    priceFeed = AggregatorV3Interface(_linkPriceFeedContract);
    blocksPerDay = _blocksPerDay;
  }

  function payForPool(uint256 _tokenSupply, uint256 _perBlockAllocation)
    external
    payable
  {
    uint256 _blockLifespan = _tokenSupply / _perBlockAllocation;
    uint256 _costUSD18 = (priceUSDPerTimePeriod18 * _blockLifespan) /
      timePeriodDays /
      blocksPerDay;
    uint256 _costWei = getProductCostWei(_costUSD18);
    if (_costWei == 0) {
      return;
    }
    require(msg.value >= _costWei, 'not enough ETH to pay for service');
    (bool success, ) = payable(owner()).call{ value: msg.value }('');
    require(success, 'could not pay for pool');
  }

  function getProductCostWei(uint256 _productCostUSD18)
    public
    view
    returns (uint256)
  {
    // adding back 18 decimals to get returned value in wei
    return (10**18 * _productCostUSD18) / _getLatestETHPrice();
  }

  /**
   * Returns the latest ETH/USD price with returned value at 18 decimals
   * https://docs.chain.link/docs/get-the-latest-price/
   */
  function _getLatestETHPrice() internal view returns (uint256) {
    uint8 decimals = priceFeed.decimals();
    (, int256 price, , , ) = priceFeed.latestRoundData();
    return uint256(price) * (10**18 / 10**decimals);
  }

  function setTimePeriodDays(uint256 _days) external onlyOwner {
    timePeriodDays = _days;
  }

  function setPriceUSDPerTimePeriod18(uint256 _priceUSD18) external onlyOwner {
    priceUSDPerTimePeriod18 = _priceUSD18;
  }

  function setBlocksPerDay(uint256 _blocks) external onlyOwner {
    blocksPerDay = _blocks;
  }
}

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

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/interfaces/IERC20.sol';
import '@openzeppelin/contracts/interfaces/IERC721.sol';
import '@openzeppelin/contracts/utils/math/SafeMath.sol';
import './interfaces/IOKLGFaaSTimePricing.sol';

/**
 * @title OKLGFaaSToken (sOKLG)
 * @notice Represents a contract where a token owner has put her tokens up for others to stake and earn said tokens.
 */
contract OKLGFaaSToken is ERC20 {
  using SafeMath for uint256;
  bool public contractIsRemoved = false;

  IERC20 private _rewardsToken;
  IERC20 private _stakedERC20;
  IERC721 private _stakedERC721;
  IOKLGFaaSTimePricing private _faasPricing;
  PoolInfo public pool;

  struct PoolInfo {
    address creator; // address of contract creator
    address tokenOwner; // address of original rewards token owner
    uint256 poolTotalSupply; // supply of rewards tokens put up to be rewarded by original owner
    uint256 poolRemainingSupply; // current supply of rewards
    uint256 totalTokensStaked; // current amount of tokens staked
    uint256 creationBlock; // block this contract was created
    uint256 perBlockNum; // amount of rewards tokens rewarded per block
    uint256 lockedUntilDate; // unix timestamp of how long this contract is locked and can't be changed
    // uint256 allocPoint; // How many allocation points assigned to this pool. ERC20s to distribute per block.
    uint256 lastRewardBlock; // Last block number that ERC20s distribution occurs.
    uint256 accERC20PerShare; // Accumulated ERC20s per share, times 1e36.
    uint256 stakeTimeLockSec; // number of seconds after depositing the user is required to stake before unstaking
    bool isStakedNft;
  }

  struct StakerInfo {
    uint256 amountStaked;
    uint256 blockOriginallyStaked; // block the user originally staked
    uint256 timeOriginallyStaked; // unix timestamp in seconds that the user originally staked
    uint256 blockLastHarvested; // the block the user last claimed/harvested rewards
    uint256 rewardDebt; // Reward debt. See explanation below.
    uint256[] nftTokenIds; // if this is an NFT staking pool, make sure we store the token IDs here
  }

  struct BlockTokenTotal {
    uint256 blockNumber;
    uint256 totalTokens;
  }

  // mapping of userAddresses => tokenAddresses that can
  // can be evaluated to determine for a particular user which tokens
  // they are staking.
  mapping(address => StakerInfo) public stakers;

  // If we need to keep track of owed rewards for a user but not
  // send them yet (i.e. when adding tokens for a stake during lockup)
  // this keeps track of that, and will add said rewards back to
  // the rewards pool on emergency unstake
  mapping(address => uint256) public rewardVault;

  event Deposit(address indexed user, uint256 amount);
  event Withdraw(address indexed user, uint256 amount);

  /**
   * @notice The constructor for the Staking Token.
   * @param _name Name of the staking token
   * @param _symbol Name of the staking token symbol
   * @param _rewardSupply The amount of tokens to mint on construction, this should be the same as the tokens provided by the creating user.
   * @param _rewardsTokenAddy Contract address of token to be rewarded to users
   * @param _stakedTokenAddy Contract address of token to be staked by users
   * @param _originalTokenOwner Address of user putting up staking tokens to be staked
   * @param _perBlockAmount Amount of tokens to be rewarded per block
   * @param _lockedUntilDate Unix timestamp that the staked tokens will be locked. 0 means locked forever until all tokens are staked
   * @param _stakeTimeLockSec number of seconds a user is required to stake, or 0 if none
   * @param _isStakedNft is this an NFT staking pool
   * @param _pricingContract is contract we use to pay to create and update supply for FaaS pools
   */
  constructor(
    string memory _name,
    string memory _symbol,
    uint256 _rewardSupply,
    address _rewardsTokenAddy,
    address _stakedTokenAddy,
    address _originalTokenOwner,
    uint256 _perBlockAmount,
    uint256 _lockedUntilDate,
    uint256 _stakeTimeLockSec,
    bool _isStakedNft,
    address _pricingContract
  ) ERC20(_name, _symbol) {
    require(
      _perBlockAmount > uint256(0) && _perBlockAmount <= uint256(_rewardSupply),
      'per block amount must be more than 0 and less than supply'
    );

    // A locked date of '0' corresponds to being locked forever until the supply has expired and been rewards to all stakers
    require(
      _lockedUntilDate > block.timestamp || _lockedUntilDate == 0,
      'locked time must be after now or 0'
    );

    _rewardsToken = IERC20(_rewardsTokenAddy);
    if (_isStakedNft) {
      _stakedERC721 = IERC721(_stakedTokenAddy);
    } else {
      _stakedERC20 = IERC20(_stakedTokenAddy);
    }

    pool = PoolInfo({
      creator: msg.sender,
      tokenOwner: _originalTokenOwner,
      poolTotalSupply: _rewardSupply,
      poolRemainingSupply: _rewardSupply,
      totalTokensStaked: 0,
      creationBlock: 0,
      perBlockNum: _perBlockAmount,
      lockedUntilDate: _lockedUntilDate,
      lastRewardBlock: block.number,
      accERC20PerShare: 0,
      stakeTimeLockSec: _stakeTimeLockSec,
      isStakedNft: _isStakedNft
    });

    _faasPricing = IOKLGFaaSTimePricing(_pricingContract);
  }

  // SHOULD ONLY BE CALLED AT CONTRACT CREATION and allows changing
  // the initial supply if tokenomics of token transfer causes
  // the original staking contract supply to be less than the original
  function updateSupply(uint256 _newSupply) external {
    require(
      msg.sender == pool.creator,
      'only contract creator can update the supply'
    );
    pool.poolTotalSupply = _newSupply;
    pool.poolRemainingSupply = _newSupply;
  }

  function addToSupply(uint256 _additionalSupply) external payable {
    require(_additionalSupply >= pool.perBlockNum, 'must add 1 block at least');
    _faasPricing.payForPool{ value: msg.value }(
      _additionalSupply,
      pool.perBlockNum
    );

    uint256 _balBefore = _rewardsToken.balanceOf(address(this));
    _rewardsToken.transferFrom(msg.sender, address(this), _additionalSupply);
    _additionalSupply = _rewardsToken.balanceOf(address(this)) - _balBefore;

    pool.poolTotalSupply += _additionalSupply;
    pool.poolRemainingSupply += _additionalSupply;
  }

  function stakedTokenAddress() external view returns (address) {
    return pool.isStakedNft ? address(_stakedERC721) : address(_stakedERC20);
  }

  function rewardsTokenAddress() external view returns (address) {
    return address(_rewardsToken);
  }

  function tokenOwner() external view returns (address) {
    return pool.tokenOwner;
  }

  function getLockedUntilDate() external view returns (uint256) {
    return pool.lockedUntilDate;
  }

  function removeStakeableTokens() external {
    require(
      msg.sender == pool.creator || msg.sender == pool.tokenOwner,
      'caller must be the contract creator or owner to remove stakable tokens'
    );
    _rewardsToken.transfer(pool.tokenOwner, pool.poolRemainingSupply);
    pool.poolRemainingSupply = 0;
    contractIsRemoved = true;
  }

  function stakeTokens(uint256 _amount, uint256[] memory _tokenIds) public {
    require(
      getLastStakableBlock() > block.number,
      'this farm is expired and no more stakers can be added'
    );

    StakerInfo storage _staker = stakers[msg.sender];
    _updatePool();

    if (balanceOf(msg.sender) > 0) {
      _harvestTokens(
        msg.sender,
        block.timestamp >=
          _staker.timeOriginallyStaked.add(pool.stakeTimeLockSec)
      );
    }

    uint256 _finalAmountTransferred;
    if (pool.isStakedNft) {
      require(
        _tokenIds.length > 0,
        "you need to provide NFT token IDs you're staking"
      );
      for (uint256 _i = 0; _i < _tokenIds.length; _i++) {
        _stakedERC721.transferFrom(msg.sender, address(this), _tokenIds[_i]);
      }

      _finalAmountTransferred = _tokenIds.length;
    } else {
      uint256 _contractBalanceBefore = _stakedERC20.balanceOf(address(this));
      _stakedERC20.transferFrom(msg.sender, address(this), _amount);

      // in the event a token contract on transfer taxes, burns, etc. tokens
      // the contract might not get the entire amount that the user originally
      // transferred. Need to calculate from the previous contract balance
      // so we know how many were actually transferred.
      _finalAmountTransferred = _stakedERC20.balanceOf(address(this)).sub(
        _contractBalanceBefore
      );
    }

    if (totalSupply() == 0) {
      pool.creationBlock = block.number;
      pool.lastRewardBlock = block.number;
    }
    _mint(msg.sender, _finalAmountTransferred);
    _staker.amountStaked = _staker.amountStaked.add(_finalAmountTransferred);
    _staker.blockOriginallyStaked = block.number;
    _staker.timeOriginallyStaked = block.timestamp;
    _staker.blockLastHarvested = block.number;
    _staker.rewardDebt = _staker.amountStaked.mul(pool.accERC20PerShare).div(
      1e36
    );
    for (uint256 _i = 0; _i < _tokenIds.length; _i++) {
      _staker.nftTokenIds.push(_tokenIds[_i]);
    }
    _updNumStaked(_finalAmountTransferred, 'add');
    emit Deposit(msg.sender, _finalAmountTransferred);
  }

  // pass 'false' for _shouldHarvest for emergency unstaking without claiming rewards
  function unstakeTokens(uint256 _amount, bool _shouldHarvest) external {
    StakerInfo memory _staker = stakers[msg.sender];
    uint256 _userBalance = _staker.amountStaked;
    require(
      pool.isStakedNft ? true : _amount <= _userBalance,
      'user can only unstake amount they have currently staked or less'
    );

    // allow unstaking if the user is emergency unstaking and not getting rewards or
    // if theres a time lock that it's past the time lock or
    // the contract rewards were removed by the original contract creator or
    // the contract is expired
    require(
      !_shouldHarvest ||
        block.timestamp >=
        _staker.timeOriginallyStaked.add(pool.stakeTimeLockSec) ||
        contractIsRemoved ||
        block.number > getLastStakableBlock(),
      'you have not staked for minimum time lock yet and the pool is not expired'
    );

    _updatePool();

    if (_shouldHarvest) {
      _harvestTokens(msg.sender, true);
    } else {
      _removeFromVaultBackToPool(msg.sender);
    }

    uint256 _amountToRemoveFromStaked = pool.isStakedNft
      ? _userBalance
      : _amount;
    _burn(
      msg.sender,
      _amountToRemoveFromStaked > balanceOf(msg.sender)
        ? balanceOf(msg.sender)
        : _amountToRemoveFromStaked
    );
    if (pool.isStakedNft) {
      for (uint256 _i = 0; _i < _staker.nftTokenIds.length; _i++) {
        _stakedERC721.transferFrom(
          address(this),
          msg.sender,
          _staker.nftTokenIds[_i]
        );
      }
    } else {
      require(
        _stakedERC20.transfer(msg.sender, _amountToRemoveFromStaked),
        'unable to send user original tokens'
      );
    }

    if (balanceOf(msg.sender) <= 0) {
      delete stakers[msg.sender];
    } else {
      _staker.amountStaked = _staker.amountStaked.sub(
        _amountToRemoveFromStaked
      );
    }
    _updNumStaked(_amountToRemoveFromStaked, 'remove');
    emit Withdraw(msg.sender, _amountToRemoveFromStaked);
  }

  function emergencyUnstake() external {
    StakerInfo memory _staker = stakers[msg.sender];
    _removeFromVaultBackToPool(msg.sender);
    uint256 _amountToRemoveFromStaked = _staker.amountStaked;
    require(
      _amountToRemoveFromStaked > 0,
      'user can only unstake if they have tokens in the pool'
    );
    _burn(
      msg.sender,
      _amountToRemoveFromStaked > balanceOf(msg.sender)
        ? balanceOf(msg.sender)
        : _amountToRemoveFromStaked
    );
    if (pool.isStakedNft) {
      for (uint256 _i = 0; _i < _staker.nftTokenIds.length; _i++) {
        _stakedERC721.transferFrom(
          address(this),
          msg.sender,
          _staker.nftTokenIds[_i]
        );
      }
    } else {
      require(
        _stakedERC20.transfer(msg.sender, _amountToRemoveFromStaked),
        'unable to send user original tokens'
      );
    }

    delete stakers[msg.sender];
    _updNumStaked(_amountToRemoveFromStaked, 'remove');
    emit Withdraw(msg.sender, _amountToRemoveFromStaked);
  }

  function harvestForUser(address _userAddy, bool _autoCompound)
    external
    returns (uint256)
  {
    require(
      msg.sender == pool.creator || msg.sender == _userAddy,
      'can only harvest tokens for someone else if this was the contract creator'
    );
    _updatePool();
    StakerInfo memory _staker = stakers[_userAddy];
    uint256 _tokensToUser = _harvestTokens(
      _userAddy,
      block.timestamp >= _staker.timeOriginallyStaked.add(pool.stakeTimeLockSec)
    );

    if (
      _autoCompound &&
      !pool.isStakedNft &&
      address(_rewardsToken) == address(_stakedERC20)
    ) {
      uint256[] memory _placeholder;
      stakeTokens(_tokensToUser, _placeholder);
    }

    return _tokensToUser;
  }

  function getLastStakableBlock() public view returns (uint256) {
    uint256 _blockToAdd = pool.creationBlock == 0
      ? block.number
      : pool.creationBlock;
    return pool.poolTotalSupply.div(pool.perBlockNum).add(_blockToAdd);
  }

  function calcHarvestTot(address _userAddy) public view returns (uint256) {
    StakerInfo memory _staker = stakers[_userAddy];

    if (
      _staker.blockLastHarvested >= block.number ||
      _staker.blockOriginallyStaked == 0 ||
      pool.totalTokensStaked == 0
    ) {
      return 0;
    }

    uint256 _accERC20PerShare = pool.accERC20PerShare;

    if (block.number > pool.lastRewardBlock && pool.totalTokensStaked != 0) {
      uint256 _endBlock = getLastStakableBlock();
      uint256 _lastBlock = block.number < _endBlock ? block.number : _endBlock;
      uint256 _nrOfBlocks = _lastBlock.sub(pool.lastRewardBlock);
      uint256 _erc20Reward = _nrOfBlocks.mul(pool.perBlockNum);
      _accERC20PerShare = _accERC20PerShare.add(
        _erc20Reward.mul(1e36).div(pool.totalTokensStaked)
      );
    }

    return
      _staker.amountStaked.mul(_accERC20PerShare).div(1e36).sub(
        _staker.rewardDebt
      );
  }

  // Update reward variables of the given pool to be up-to-date.
  function _updatePool() private {
    uint256 _endBlock = getLastStakableBlock();
    uint256 _lastBlock = block.number < _endBlock ? block.number : _endBlock;

    if (_lastBlock <= pool.lastRewardBlock) {
      return;
    }
    uint256 _stakedSupply = pool.totalTokensStaked;
    if (_stakedSupply == 0) {
      pool.lastRewardBlock = _lastBlock;
      return;
    }

    uint256 _nrOfBlocks = _lastBlock.sub(pool.lastRewardBlock);
    uint256 _erc20Reward = _nrOfBlocks.mul(pool.perBlockNum);

    pool.accERC20PerShare = pool.accERC20PerShare.add(
      _erc20Reward.mul(1e36).div(_stakedSupply)
    );
    pool.lastRewardBlock = _lastBlock;
  }

  function _harvestTokens(address _userAddy, bool _sendRewards)
    private
    returns (uint256)
  {
    StakerInfo storage _staker = stakers[_userAddy];
    require(_staker.blockOriginallyStaked > 0, 'user must have tokens staked');

    uint256 _num2Trans = calcHarvestTot(_userAddy);
    if (_num2Trans > 0) {
      if (_sendRewards) {
        _sendRewardsToUser(_userAddy, _num2Trans);
      } else {
        rewardVault[_userAddy] += _num2Trans;
      }
    }
    _staker.rewardDebt = _staker.amountStaked.mul(pool.accERC20PerShare).div(
      1e36
    );
    _staker.blockLastHarvested = block.number;
    return _num2Trans;
  }

  function _sendRewardsToUser(address _user, uint256 _amount) internal {
    uint256 _totalToSend = _amount + rewardVault[_user];
    rewardVault[_user] = 0;
    require(
      _rewardsToken.transfer(_user, _totalToSend),
      'unable to send user their harvested tokens'
    );
    pool.poolRemainingSupply = pool.poolRemainingSupply.sub(_totalToSend);
  }

  function _removeFromVaultBackToPool(address _user) internal {
    uint256 _amountInVault = rewardVault[_user];
    rewardVault[_user] = 0;
    pool.poolRemainingSupply = pool.poolRemainingSupply.add(_amountInVault);
  }

  // update the amount currently staked after a user harvests
  function _updNumStaked(uint256 _amount, string memory _operation) private {
    if (_compareStr(_operation, 'remove')) {
      pool.totalTokensStaked = pool.totalTokensStaked.sub(_amount);
    } else {
      pool.totalTokensStaked = pool.totalTokensStaked.add(_amount);
    }
  }

  function _compareStr(string memory a, string memory b)
    private
    pure
    returns (bool)
  {
    return (keccak256(abi.encodePacked((a))) ==
      keccak256(abi.encodePacked((b))));
  }
}

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

import '@openzeppelin/contracts/interfaces/IERC20.sol';
import './interfaces/IOKLGSpend.sol';
import './OKLGWithdrawable.sol';

/**
 * @title OKLGProduct
 * @dev Contract that every product developed in the OKLG ecosystem should implement
 */
contract OKLGProduct is OKLGWithdrawable {
  IERC20 private _token; // OKLG
  IOKLGSpend private _spend;

  uint8 public productID;

  constructor(
    uint8 _productID,
    address _tokenAddy,
    address _spendAddy
  ) {
    productID = _productID;
    _token = IERC20(_tokenAddy);
    _spend = IOKLGSpend(_spendAddy);
  }

  function setTokenAddy(address _tokenAddy) external onlyOwner {
    _token = IERC20(_tokenAddy);
  }

  function setSpendAddy(address _spendAddy) external onlyOwner {
    _spend = IOKLGSpend(_spendAddy);
  }

  function setProductID(uint8 _newId) external onlyOwner {
    productID = _newId;
  }

  function getTokenAddress() public view returns (address) {
    return address(_token);
  }

  function getSpendAddress() public view returns (address) {
    return address(_spend);
  }

  function _payForService(uint256 _weiToRemoveFromSpend) internal {
    _spend.spendOnProduct{ value: msg.value - _weiToRemoveFromSpend }(
      msg.sender,
      productID
    );
  }
}

File 5 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 6 of 18 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

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

  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 7 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 8 of 18 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

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

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

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

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

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

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

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

    /**
     * @dev Spend `amount` form the allowance of `owner` toward `spender`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

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

File 9 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";

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

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";

File 11 of 18 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

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

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

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

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

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

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

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

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

File 12 of 18 : IOKLGFaaSTimePricing.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IOKLGFaaSTimePricing {
  function payForPool(uint256 supply, uint256 perBlockAllocation)
    external
    payable;

  function getProductCostWei(uint256 _productCostUSD18)
    external
    view
    returns (uint256);
}

File 13 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 14 of 18 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 15 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

File 17 of 18 : IOKLGSpend.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title IOKLGSpend
 * @dev Logic for spending OKLG on products in the product ecosystem.
 */
interface IOKLGSpend {
  function spendOnProduct(address _payor, uint8 _product) external payable;
}

File 18 of 18 : OKLGWithdrawable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/interfaces/IERC20.sol';

/**
 * @title OKLGWithdrawable
 * @dev Supports being able to get tokens or ETH out of a contract with ease
 */
contract OKLGWithdrawable is Ownable {
  function withdrawTokens(address _tokenAddy, uint256 _amount)
    external
    onlyOwner
  {
    IERC20 _token = IERC20(_tokenAddy);
    _amount = _amount > 0 ? _amount : _token.balanceOf(address(this));
    require(_amount > 0, 'make sure there is a balance available to withdraw');
    _token.transfer(owner(), _amount);
  }

  function withdrawETH() external onlyOwner {
    payable(owner()).call{ value: address(this).balance }('');
  }
}

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

Contract ABI

[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_spendAddress","type":"address"},{"internalType":"address","name":"_linkPriceFeedContract","type":"address"},{"internalType":"uint256","name":"_blocksPerDay","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allFarmingContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsTokenAddy","type":"address"},{"internalType":"address","name":"_stakedTokenAddy","type":"address"},{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"uint256","name":"_perBlockAllocation","type":"uint256"},{"internalType":"uint256","name":"_lockedUntilDate","type":"uint256"},{"internalType":"uint256","name":"_timelockSeconds","type":"uint256"},{"internalType":"bool","name":"_isStakedNft","type":"bool"}],"name":"createNewTokenContract","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getAllFarmingContracts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFaasPricing","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSpendAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getTokensForStaking","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"productID","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_faasTokenAddy","type":"address"}],"name":"removeTokenContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_newId","type":"uint8"}],"name":"setProductID","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spendAddy","type":"address"}],"name":"setSpendAddy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddy","type":"address"}],"name":"setTokenAddy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokensUpForStaking","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakingContracts","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":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddy","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620050f1380380620050f18339810160408190526200003491620001c4565b60088484620000433362000149565b60028054600180546001600160a01b03199081166001600160a01b03968716179091556001600160a81b0319909116600160a01b60ff969096169590950216939093179116179055604051829082906200009d9062000199565b6001600160a01b0390921682526020820152604001604051809103906000f080158015620000cf573d6000803e3d6000fd5b50600680546001600160a01b0319166001600160a01b0392909216918217905560405163f2fde38b60e01b815233600482015263f2fde38b90602401600060405180830381600087803b1580156200012657600080fd5b505af11580156200013b573d6000803e3d6000fd5b505050505050505062000215565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6109ea806200470783390190565b80516001600160a01b0381168114620001bf57600080fd5b919050565b60008060008060808587031215620001da578384fd5b620001e585620001a7565b9350620001f560208601620001a7565b92506200020560408601620001a7565b6060959095015193969295505050565b6144e280620002256000396000f3fe6080604052600436106200012b5760003560e01c80637c0bf7bb11620000ad578063dc3aaab5116200006c578063dc3aaab51462000337578063e086e5ec1462000357578063f2fde38b146200036f578063f3c8f7991462000394578063f9fb452f14620003ac57600080fd5b80637c0bf7bb1462000281578063842a84bf14620002a65780638da5cb5b14620002cb5780639e9f695d14620002eb578063b1e6cb82146200031057600080fd5b80634bc10ccb11620000fa5780634bc10ccb14620001e357806351dd8dd01462000208578063571d959d146200021f578063715018a614620002445780637614d4e2146200025c57600080fd5b806306b091f9146200013057806310fe9ae814620001575780631638a085146200018f57806325d386a514620001c3575b600080fd5b3480156200013d57600080fd5b50620001556200014f36600462001257565b620003e2565b005b3480156200016457600080fd5b506001546001600160a01b03165b6040516001600160a01b0390911681526020015b60405180910390f35b3480156200019c57600080fd5b50620001b4620001ae3660046200119a565b620005bd565b604051620001869190620012f9565b348015620001d057600080fd5b506006546001600160a01b031662000172565b348015620001f057600080fd5b5062000155620002023660046200119a565b62000635565b6200015562000219366004620011df565b62000684565b3480156200022c57600080fd5b50620001556200023e3660046200119a565b62000b7c565b3480156200025157600080fd5b506200015562000e72565b3480156200026957600080fd5b50620001726200027b36600462001257565b62000ead565b3480156200028e57600080fd5b5062000155620002a03660046200119a565b62000ee6565b348015620002b357600080fd5b5062000172620002c5366004620012a4565b62000f35565b348015620002d857600080fd5b506000546001600160a01b031662000172565b348015620002f857600080fd5b50620001556200030a366004620012d6565b62000f60565b3480156200031d57600080fd5b506200032860055481565b60405190815260200162000186565b3480156200034457600080fd5b506002546001600160a01b031662000172565b3480156200036457600080fd5b506200015562000fad565b3480156200037c57600080fd5b50620001556200038e3660046200119a565b62001036565b348015620003a157600080fd5b50620001b4620010d8565b348015620003b957600080fd5b50600254620003cf90600160a01b900460ff1681565b60405160ff909116815260200162000186565b6000546001600160a01b03163314620004185760405162461bcd60e51b81526004016200040f9062001409565b60405180910390fd5b8181620004a0576040516370a0823160e01b81523060048201526001600160a01b038216906370a082319060240160206040518083038186803b1580156200045f57600080fd5b505afa15801562000474573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200049a9190620012bd565b620004a2565b815b915060008211620005115760405162461bcd60e51b815260206004820152603260248201527f6d616b65207375726520746865726520697320612062616c616e636520617661604482015271696c61626c6520746f20776974686472617760701b60648201526084016200040f565b806001600160a01b031663a9059cbb620005336000546001600160a01b031690565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101859052604401602060405180830381600087803b1580156200057c57600080fd5b505af115801562000591573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005b7919062001285565b50505050565b6001600160a01b0381166000908152600360209081526040918290208054835181840281018401909452808452606093928301828280156200062957602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116200060a575b50505050509050919050565b6000546001600160a01b03163314620006625760405162461bcd60e51b81526004016200040f9062001409565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600654604051631de66df760e21b815260048101879052602481018690526001600160a01b0390911690637799b7dc9034906044016000604051808303818588803b158015620006d357600080fd5b505af1158015620006e8573d6000803e3d6000fd5b50506040516323b872dd60e01b8152336004820152306024820152604481018990528a93506001600160a01b03841692506323b872dd9150606401602060405180830381600087803b1580156200073e57600080fd5b505af115801562000753573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000779919062001285565b506040516370a0823160e01b81523060048201526000906001600160a01b038316906370a082319060240160206040518083038186803b158015620007bd57600080fd5b505afa158015620007d2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007f89190620012bd565b87111562000881576040516370a0823160e01b81523060048201526001600160a01b038316906370a082319060240160206040518083038186803b1580156200084057600080fd5b505afa15801562000855573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200087b9190620012bd565b62000883565b865b90506000818a8a338a8a8a8a600660009054906101000a90046001600160a01b0316604051620008b3906200118c565b620008c79998979695949392919062001348565b604051809103906000f080158015620008e4573d6000803e3d6000fd5b506004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b038085166001600160a01b031992831681179093558d166000908152600360209081526040822080549586018155825281209093018054909116909117905560058054929350906200096e8362001458565b909155505060405163a9059cbb60e01b81526001600160a01b0382811660048301526024820184905284169063a9059cbb90604401602060405180830381600087803b158015620009be57600080fd5b505af1158015620009d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620009f9919062001285565b506040516370a0823160e01b81526001600160a01b038281166004830152600091908516906370a082319060240160206040518083038186803b15801562000a4057600080fd5b505afa15801562000a55573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a7b9190620012bd565b83111562000b06576040516370a0823160e01b81526001600160a01b0383811660048301528516906370a082319060240160206040518083038186803b15801562000ac557600080fd5b505afa15801562000ada573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000b009190620012bd565b62000b08565b825b905080831462000b6f57604051636bd0804960e01b8152600481018290526001600160a01b03831690636bd0804990602401600060405180830381600087803b15801562000b5557600080fd5b505af115801562000b6a573d6000803e3d6000fd5b505050505b5050505050505050505050565b6000819050806001600160a01b031663a3e676106040518163ffffffff1660e01b815260040160206040518083038186803b15801562000bbb57600080fd5b505afa15801562000bd0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000bf69190620011c0565b6001600160a01b0316336001600160a01b03161462000c775760405162461bcd60e51b815260206004820152603660248201527f75736572206d75737420626520746865206f726967696e616c20746f6b656e206044820152756f776e657220746f2072656d6f766520746f6b656e7360501b60648201526084016200040f565b806001600160a01b031663cadc6c596040518163ffffffff1660e01b815260040160206040518083038186803b15801562000cb157600080fd5b505afa15801562000cc6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000cec9190620012bd565b4211801562000d6e5750806001600160a01b031663cadc6c596040518163ffffffff1660e01b815260040160206040518083038186803b15801562000d3057600080fd5b505afa15801562000d45573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d6b9190620012bd565b15155b62000e015760405162461bcd60e51b815260206004820152605660248201527f6974206d75737420626520616674657220746865206c6f636b65642074696d6560448201527f207468652075736572206f726967696e616c6c7920636f6e666967757265642060648201527530b732103737ba103637b1b5b2b2103337b932bb32b960511b608482015260a4016200040f565b806001600160a01b031663e85c77bb6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562000e3d57600080fd5b505af115801562000e52573d6000803e3d6000fd5b50506005805492509050600062000e69836200143e565b91905055505050565b6000546001600160a01b0316331462000e9f5760405162461bcd60e51b81526004016200040f9062001409565b62000eab60006200113c565b565b6003602052816000526040600020818154811062000eca57600080fd5b6000918252602090912001546001600160a01b03169150829050565b6000546001600160a01b0316331462000f135760405162461bcd60e51b81526004016200040f9062001409565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6004818154811062000f4657600080fd5b6000918252602090912001546001600160a01b0316905081565b6000546001600160a01b0316331462000f8d5760405162461bcd60e51b81526004016200040f9062001409565b6002805460ff909216600160a01b0260ff60a01b19909216919091179055565b6000546001600160a01b0316331462000fda5760405162461bcd60e51b81526004016200040f9062001409565b6000546001600160a01b03166001600160a01b03164760405160006040518083038185875af1925050503d806000811462001031576040519150601f19603f3d011682016040523d82523d6000602084013e505050565b505050565b6000546001600160a01b03163314620010635760405162461bcd60e51b81526004016200040f9062001409565b6001600160a01b038116620010ca5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016200040f565b620010d5816200113c565b50565b606060048054806020026020016040519081016040528092919081815260200182805480156200113257602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001113575b5050505050905090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61302480620014b283390190565b600060208284031215620011ac578081fd5b8135620011b9816200148c565b9392505050565b600060208284031215620011d2578081fd5b8151620011b9816200148c565b600080600080600080600060e0888a031215620011fa578283fd5b873562001207816200148c565b9650602088013562001219816200148c565b955060408801359450606088013593506080880135925060a0880135915060c08801356200124781620014a2565b8091505092959891949750929550565b600080604083850312156200126a578182fd5b823562001277816200148c565b946020939093013593505050565b60006020828403121562001297578081fd5b8151620011b981620014a2565b600060208284031215620012b6578081fd5b5035919050565b600060208284031215620012cf578081fd5b5051919050565b600060208284031215620012e8578081fd5b813560ff81168114620011b9578182fd5b6020808252825182820181905260009190848201906040850190845b818110156200133c5783516001600160a01b03168352928401929184019160010162001315565b50909695505050505050565b6101608082526012908201527127a5a6239029ba30b5b4b733902a37b5b2b760711b6101808201526101a06020820181905260059082015264734f4b4c4760d81b6101c0820152604081018a90526001600160a01b03891660608201526101e081016001600160a01b03891660808301526001600160a01b03881660a08301528660c08301528560e083015284610100830152620013eb61012083018515159052565b6001600160a01b0383166101408301529a9950505050505050505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60008162001450576200145062001476565b506000190190565b60006000198214156200146f576200146f62001476565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114620010d557600080fd5b8015158114620010d557600080fdfe60806040526005805460ff191690553480156200001b57600080fd5b5060405162003024380380620030248339810160408190526200003e9162000432565b8a518b908b9062000057906003906020850190620002ab565b5080516200006d906004906020840190620002ab565b505050600085118015620000815750888511155b620000f95760405162461bcd60e51b815260206004820152603960248201527f70657220626c6f636b20616d6f756e74206d757374206265206d6f726520746860448201527f616e203020616e64206c657373207468616e20737570706c790000000000000060648201526084015b60405180910390fd5b4284118062000106575083155b6200015f5760405162461bcd60e51b815260206004820152602260248201527f6c6f636b65642074696d65206d757374206265206166746572206e6f77206f72604482015261020360f41b6064820152608401620000f0565b60058054610100600160a81b0319166101006001600160a01b038b16021790558115620001a757600780546001600160a01b0319166001600160a01b038916179055620001c3565b600680546001600160a01b0319166001600160a01b0389161790555b6040805161018081018252338082526001600160a01b03988916602083018190529282018c9052606082018c905260006080830181905260a0830181905260c0830189905260e0830188905243610100840181905261012084018290526101408401889052951515610160909301839052600980546001600160a01b03199081169093179055600a80548316909417909355600b8c9055600c9b909b55600d829055600e829055600f969096556010949094556011919091556012929092556013556014805460ff191690921790915560088054909516911617909255506200057992505050565b828054620002b99062000526565b90600052602060002090601f016020900481019282620002dd576000855562000328565b82601f10620002f857805160ff191683800117855562000328565b8280016001018555821562000328579182015b82811115620003285782518255916020019190600101906200030b565b50620003369291506200033a565b5090565b5b808211156200033657600081556001016200033b565b80516001600160a01b03811681146200036957600080fd5b919050565b805180151581146200036957600080fd5b600082601f83011262000390578081fd5b81516001600160401b0380821115620003ad57620003ad62000563565b604051601f8301601f19908116603f01168101908282118183101715620003d857620003d862000563565b81604052838152602092508683858801011115620003f4578485fd5b8491505b83821015620004175785820183015181830184015290820190620003f8565b838211156200042857848385830101525b9695505050505050565b60008060008060008060008060008060006101608c8e03121562000454578687fd5b8b516001600160401b038111156200046a578788fd5b620004788e828f016200037f565b60208e0151909c5090506001600160401b0381111562000496578788fd5b620004a48e828f016200037f565b9a505060408c01519850620004bc60608d0162000351565b9750620004cc60808d0162000351565b9650620004dc60a08d0162000351565b955060c08c0151945060e08c015193506101008c01519250620005036101208d016200036e565b9150620005146101408d0162000351565b90509295989b509295989b9093969950565b600181811c908216806200053b57607f821691505b602082108114156200055d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b612a9b80620005896000396000f3fe6080604052600436106101b75760003560e01c806395d89b41116100ec578063dd62ed3e1161008a578063e93793f611610064578063e93793f6146105c2578063efd703a7146105e5578063fd7db85414610612578063ff333a761461063257600080fd5b8063dd62ed3e14610552578063e17c0f1114610598578063e85c77bb146105ad57600080fd5b8063a457c2d7116100c6578063a457c2d7146104dd578063a9059cbb146104fd578063b7f1ef1e1461051d578063cadc6c591461053d57600080fd5b806395d89b411461047c57806396e231e914610491578063a3e67610146104ab57600080fd5b8063431a45321161015957806370a082311161013357806370a082311461039f5780637589cf2f146103d55780638bdfe956146103ea5780639168ae721461040a57600080fd5b8063431a45321461034a5780634afcb5371461035f5780636bd080491461037f57600080fd5b806318160ddd1161019557806318160ddd146102cf57806323b872dd146102ee578063313ce5671461030e578063395093511461032a57600080fd5b806306fdde03146101bc578063095ea7b3146101e757806316f0115b14610217575b600080fd5b3480156101c857600080fd5b506101d1610647565b6040516101de91906128ee565b60405180910390f35b3480156101f357600080fd5b50610207610202366004612749565b6106d9565b60405190151581526020016101de565b34801561022357600080fd5b50600954600a54600b54600c54600d54600e54600f546010546011546012546013546014546102699b6001600160a01b039081169b169998979695949392919060ff168c565b604080516001600160a01b039d8e1681529c909b1660208d0152998b019890985260608a0196909652608089019490945260a088019290925260c087015260e08601526101008501526101208401526101408301521515610160820152610180016101de565b3480156102db57600080fd5b506002545b6040519081526020016101de565b3480156102fa57600080fd5b506102076103093660046126d8565b6106f1565b34801561031a57600080fd5b50604051601281526020016101de565b34801561033657600080fd5b50610207610345366004612749565b610715565b61035d61035836600461278e565b610754565b005b34801561036b57600080fd5b506102e061037a36600461268c565b6109ee565b34801561038b57600080fd5b5061035d61039a36600461278e565b610b9f565b3480156103ab57600080fd5b506102e06103ba36600461268c565b6001600160a01b031660009081526020819052604090205490565b3480156103e157600080fd5b5061035d610c17565b3480156103f657600080fd5b506102e0610405366004612713565b610f6d565b34801561041657600080fd5b5061045461042536600461268c565b601560205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a0016101de565b34801561048857600080fd5b506101d161113c565b34801561049d57600080fd5b506005546102079060ff1681565b3480156104b757600080fd5b50600a546001600160a01b03165b6040516001600160a01b0390911681526020016101de565b3480156104e957600080fd5b506102076104f8366004612749565b61114b565b34801561050957600080fd5b50610207610518366004612749565b6111dd565b34801561052957600080fd5b5061035d6105383660046127be565b6111eb565b34801561054957600080fd5b506010546102e0565b34801561055e57600080fd5b506102e061056d3660046126a6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3480156105a457600080fd5b506102e0611686565b3480156105b957600080fd5b5061035d6116c2565b3480156105ce57600080fd5b5060055461010090046001600160a01b03166104c5565b3480156105f157600080fd5b506102e061060036600461268c565b60166020526000908152604090205481565b34801561061e57600080fd5b5061035d61062d36600461288a565b61180c565b34801561063e57600080fd5b506104c5611c87565b606060038054610656906129fe565b80601f0160208091040260200160405190810160405280929190818152602001828054610682906129fe565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b5050505050905090565b6000336106e7818585611cb4565b5060019392505050565b6000336106ff858285611dd9565b61070a858585611e6b565b506001949350505050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091906106e7908290869061074f908790612964565b611cb4565b600f548110156107ab5760405162461bcd60e51b815260206004820152601960248201527f6d75737420616464203120626c6f636b206174206c656173740000000000000060448201526064015b60405180910390fd5b600854600f54604051631de66df760e21b81526001600160a01b0390921691637799b7dc9134916107e9918691600401918252602082015260400190565b6000604051808303818588803b15801561080257600080fd5b505af1158015610816573d6000803e3d6000fd5b50506005546040516370a0823160e01b8152306004820152600094506101009091046001600160a01b031692506370a08231915060240160206040518083038186803b15801561086557600080fd5b505afa158015610879573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089d91906127a6565b6005546040516323b872dd60e01b815291925061010090046001600160a01b0316906323b872dd906108d7903390309087906004016128ca565b602060405180830381600087803b1580156108f157600080fd5b505af1158015610905573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109299190612772565b506005546040516370a0823160e01b8152306004820152829161010090046001600160a01b0316906370a082319060240160206040518083038186803b15801561097257600080fd5b505afa158015610986573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109aa91906127a6565b6109b491906129bb565b915081600960020160008282546109cb9190612964565b9091555050600c80548391906000906109e5908490612964565b90915550505050565b6001600160a01b0381166000908152601560209081526040808320815160c0810183528154815260018201548185015260028201548184015260038201546060820152600482015460808201526005820180548451818702810187019095528085528695929460a086019390929190830182828015610a8c57602002820191906000526020600020905b815481526020019060010190808311610a78575b5050505050815250509050438160600151101580610aac57506020810151155b80610ab75750600d54155b15610ac55750600092915050565b60125460115443118015610ada5750600d5415155b15610b5f576000610ae9611686565b90506000814310610afa5781610afc565b435b601154909150600090610b10908390612039565b600f54909150600090610b2490839061204c565b600d54909150610b5890610b5190610b4b846ec097ce7bc90715b34b9f100000000061204c565b90612058565b8690612064565b9450505050505b610b978260800151610b916ec097ce7bc90715b34b9f1000000000610b4b85876000015161204c90919063ffffffff16565b90612039565b949350505050565b6009546001600160a01b03163314610c0d5760405162461bcd60e51b815260206004820152602b60248201527f6f6e6c7920636f6e74726163742063726561746f722063616e2075706461746560448201526a2074686520737570706c7960a81b60648201526084016107a2565b600b819055600c55565b336000908152601560209081526040808320815160c0810183528154815260018201548185015260028201548184015260038201546060820152600482015460808201526005820180548451818702810187019095528085529194929360a0860193909290830182828015610cab57602002820191906000526020600020905b815481526020019060010190808311610c97575b5050505050815250509050610cbf33612070565b805180610d2c5760405162461bcd60e51b815260206004820152603560248201527f757365722063616e206f6e6c7920756e7374616b6520696620746865792068616044820152741d99481d1bdad95b9cc81a5b881d1a19481c1bdbdb605a1b60648201526084016107a2565b33600081815260208190526040902054610d6391905b8311610d4e57826120a1565b336000908152602081905260409020546120a1565b60145460ff1615610e2e5760005b8260a0015151811015610e285760075460a084015180516001600160a01b03909216916323b872dd91309133919086908110610dbd57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b8152600401610de3939291906128ca565b600060405180830381600087803b158015610dfd57600080fd5b505af1158015610e11573d6000803e3d6000fd5b505050508080610e2090612a39565b915050610d71565b50610ece565b60065460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015610e7a57600080fd5b505af1158015610e8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb29190612772565b610ece5760405162461bcd60e51b81526004016107a290612921565b3360009081526015602052604081208181556001810182905560028101829055600381018290556004810182905590610f0a6005830182612636565b5050610f34816040518060400160405280600681526020016572656d6f766560d01b8152506121e7565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a25050565b6009546000906001600160a01b0316331480610f915750336001600160a01b038416145b6110155760405162461bcd60e51b815260206004820152604960248201527f63616e206f6e6c79206861727665737420746f6b656e7320666f7220736f6d6560448201527f6f6e6520656c73652069662074686973207761732074686520636f6e747261636064820152683a1031b932b0ba37b960b91b608482015260a4016107a2565b61101d612235565b6001600160a01b0383166000908152601560209081526040808320815160c0810183528154815260018201548185015260028201548184015260038201546060820152600482015460808201526005820180548451818702810187019095528085529194929360a08601939092908301828280156110ba57602002820191906000526020600020905b8154815260200190600101908083116110a6575b505050505081525050905060006110ef856110e76009600a0154856040015161206490919063ffffffff16565b4210156122cd565b9050838015611101575060145460ff16155b8015611122575060065460055461010090046001600160a01b039081169116145b15610b9757606061113382826111eb565b50949350505050565b606060048054610656906129fe565b3360008181526001602090815260408083206001600160a01b0387168452909152812054909190838110156111d05760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016107a2565b61070a8286868403611cb4565b6000336106e7818585611e6b565b436111f4611686565b1161125f5760405162461bcd60e51b815260206004820152603560248201527f74686973206661726d206973206578706972656420616e64206e6f206d6f7265604482015274081cdd185ad95c9cc818d85b881899481859191959605a1b60648201526084016107a2565b336000908152601560205260409020611276612235565b33600090815260208190526040902054156112a45760135460028201546112a29133916110e791612064565b505b60145460009060ff16156113d757600083511161131c5760405162461bcd60e51b815260206004820152603060248201527f796f75206e65656420746f2070726f76696465204e465420746f6b656e20494460448201526f7320796f75277265207374616b696e6760801b60648201526084016107a2565b60005b83518110156113ce5760075484516001600160a01b03909116906323b872dd903390309088908690811061136357634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b8152600401611389939291906128ca565b600060405180830381600087803b1580156113a357600080fd5b505af11580156113b7573d6000803e3d6000fd5b5050505080806113c690612a39565b91505061131f565b50508151611560565b6006546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561141b57600080fd5b505afa15801561142f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061145391906127a6565b6006546040516323b872dd60e01b81529192506001600160a01b0316906323b872dd9061148890339030908a906004016128ca565b602060405180830381600087803b1580156114a257600080fd5b505af11580156114b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114da9190612772565b506006546040516370a0823160e01b815230600482015261155c9183916001600160a01b03909116906370a082319060240160206040518083038186803b15801561152457600080fd5b505afa158015611538573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9191906127a6565b9150505b6002546115715743600e8190556011555b61157b33826123c5565b81546115879082612064565b808355436001840181905542600285015560038401556012546115bf916ec097ce7bc90715b34b9f100000000091610b4b919061204c565b600483015560005b835181101561162557826005018482815181106115f457634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825460018101845560009384529190922001558061161d81612a39565b9150506115c7565b5061164b816040518060400160405280600381526020016218591960ea1b8152506121e7565b60405181815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a250505050565b600e5460009081901561169b57600e5461169d565b435b600f54600b549192506116bc9183916116b69190612058565b90612064565b91505090565b6009546001600160a01b03163314806116e55750600a546001600160a01b031633145b6117665760405162461bcd60e51b815260206004820152604660248201527f63616c6c6572206d7573742062652074686520636f6e7472616374206372656160448201527f746f72206f72206f776e657220746f2072656d6f7665207374616b61626c6520606482015265746f6b656e7360d01b608482015260a4016107a2565b600554600a54600c5460405163a9059cbb60e01b81526001600160a01b0392831660048201526024810191909152610100909204169063a9059cbb90604401602060405180830381600087803b1580156117bf57600080fd5b505af11580156117d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f79190612772565b506000600c556005805460ff19166001179055565b336000908152601560209081526040808320815160c0810183528154815260018201548185015260028201548184015260038201546060820152600482015460808201526005820180548451818702810187019095528085529194929360a08601939092908301828280156118a057602002820191906000526020600020905b81548152602001906001019080831161188c575b50505091909252505081516014549293509160ff1690506118c457808411156118c7565b60015b6119395760405162461bcd60e51b815260206004820152603f60248201527f757365722063616e206f6e6c7920756e7374616b6520616d6f756e742074686560448201527f7920686176652063757272656e746c79207374616b6564206f72206c6573730060648201526084016107a2565b8215806119565750601354604083015161195291612064565b4210155b80611963575060055460ff165b806119745750611971611686565b43115b6119f85760405162461bcd60e51b815260206004820152604960248201527f796f752068617665206e6f74207374616b656420666f72206d696e696d756d2060448201527f74696d65206c6f636b2079657420616e642074686520706f6f6c206973206e6f6064820152681d08195e1c1a5c995960ba1b608482015260a4016107a2565b611a00612235565b8215611a1757611a113360016122cd565b50611a20565b611a2033612070565b60145460009060ff16611a335784611a35565b815b33600081815260208190526040902054919250611a5191610d42565b60145460ff1615611b1c5760005b8360a0015151811015611b165760075460a085015180516001600160a01b03909216916323b872dd91309133919086908110611aab57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b8152600401611ad1939291906128ca565b600060405180830381600087803b158015611aeb57600080fd5b505af1158015611aff573d6000803e3d6000fd5b505050508080611b0e90612a39565b915050611a5f565b50611bbc565b60065460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015611b6857600080fd5b505af1158015611b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba09190612772565b611bbc5760405162461bcd60e51b81526004016107a290612921565b3360009081526020819052604081205411611c14573360009081526015602052604081208181556001810182905560028101829055600381018290556004810182905590611c0d6005830182612636565b5050611c23565b8251611c209082612039565b83525b611c4b816040518060400160405280600681526020016572656d6f766560d01b8152506121e7565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a25050505050565b60145460009060ff16611ca457506006546001600160a01b031690565b506007546001600160a01b031690565b6001600160a01b038316611d165760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016107a2565b6001600160a01b038216611d775760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016107a2565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b038381166000908152600160209081526040808320938616835292905220546000198114611e655781811015611e585760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016107a2565b611e658484848403611cb4565b50505050565b6001600160a01b038316611ecf5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016107a2565b6001600160a01b038216611f315760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016107a2565b6001600160a01b03831660009081526020819052604090205481811015611fa95760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016107a2565b6001600160a01b03808516600090815260208190526040808220858503905591851681529081208054849290611fe0908490612964565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161202c91815260200190565b60405180910390a3611e65565b600061204582846129bb565b9392505050565b6000612045828461299c565b6000612045828461297c565b60006120458284612964565b6001600160a01b03811660009081526016602052604081208054919055600c5461209a9082612064565b600c555050565b6001600160a01b0382166121015760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016107a2565b6001600160a01b038216600090815260208190526040902054818110156121755760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016107a2565b6001600160a01b03831660009081526020819052604081208383039055600280548492906121a49084906129bb565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001611dcc565b61220f816040518060400160405280600681526020016572656d6f766560d01b8152506124a4565b1561222857600d546122219083612039565b600d555050565b600d546122219083612064565b600061223f611686565b905060008143106122505781612252565b435b6011549091508111612262575050565b600d5480612271575060115550565b601154600090612282908490612039565b600f5490915060009061229690839061204c565b90506122c16122b884610b4b846ec097ce7bc90715b34b9f100000000061204c565b60125490612064565b60125550505060115550565b6001600160a01b038216600090815260156020526040812060018101546123365760405162461bcd60e51b815260206004820152601c60248201527f75736572206d757374206861766520746f6b656e73207374616b65640000000060448201526064016107a2565b6000612341856109ee565b9050801561238c57831561235e5761235985826124fd565b61238c565b6001600160a01b03851660009081526016602052604081208054839290612386908490612964565b90915550505b60125482546123af916ec097ce7bc90715b34b9f100000000091610b4b9161204c565b6004830155436003909201919091559392505050565b6001600160a01b03821661241b5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016107a2565b806002600082825461242d9190612964565b90915550506001600160a01b0382166000908152602081905260408120805483929061245a908490612964565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6000816040516020016124b791906128ae565b60405160208183030381529060405280519060200120836040516020016124de91906128ae565b6040516020818303038152906040528051906020012014905092915050565b6001600160a01b0382166000908152601660205260408120546125209083612964565b6001600160a01b0384811660008181526016602052604080822091909155600554905163a9059cbb60e01b8152600481019290925260248201849052929350610100909204169063a9059cbb90604401602060405180830381600087803b15801561258a57600080fd5b505af115801561259e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c29190612772565b6126215760405162461bcd60e51b815260206004820152602a60248201527f756e61626c6520746f2073656e6420757365722074686569722068617276657360448201526974656420746f6b656e7360b01b60648201526084016107a2565b600c5461262e9082612039565b600c55505050565b50805460008255906000526020600020908101906126549190612657565b50565b5b8082111561266c5760008155600101612658565b5090565b80356001600160a01b038116811461268757600080fd5b919050565b60006020828403121561269d578081fd5b61204582612670565b600080604083850312156126b8578081fd5b6126c183612670565b91506126cf60208401612670565b90509250929050565b6000806000606084860312156126ec578081fd5b6126f584612670565b925061270360208501612670565b9150604084013590509250925092565b60008060408385031215612725578182fd5b61272e83612670565b9150602083013561273e81612a80565b809150509250929050565b6000806040838503121561275b578182fd5b61276483612670565b946020939093013593505050565b600060208284031215612783578081fd5b815161204581612a80565b60006020828403121561279f578081fd5b5035919050565b6000602082840312156127b7578081fd5b5051919050565b600080604083850312156127d0578182fd5b8235915060208084013567ffffffffffffffff808211156127ef578384fd5b818601915086601f830112612802578384fd5b81358181111561281457612814612a6a565b8060051b604051601f19603f8301168101818110858211171561283957612839612a6a565b604052828152858101935084860182860187018b1015612857578788fd5b8795505b8386101561287957803585526001959095019493860193860161285b565b508096505050505050509250929050565b6000806040838503121561289c578182fd5b82359150602083013561273e81612a80565b600082516128c08184602087016129d2565b9190910192915050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b602081526000825180602084015261290d8160408501602087016129d2565b601f01601f19169190910160400192915050565b60208082526023908201527f756e61626c6520746f2073656e642075736572206f726967696e616c20746f6b604082015262656e7360e81b606082015260800190565b6000821982111561297757612977612a54565b500190565b60008261299757634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156129b6576129b6612a54565b500290565b6000828210156129cd576129cd612a54565b500390565b60005b838110156129ed5781810151838201526020016129d5565b83811115611e655750506000910152565b600181811c90821680612a1257607f821691505b60208210811415612a3357634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415612a4d57612a4d612a54565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b801515811461265457600080fdfea164736f6c6343000804000aa164736f6c6343000804000a6080604052601e600255681043561a882930000060035534801561002257600080fd5b506040516109ea3803806109ea833981016040819052610041916100c3565b61004a33610073565b600180546001600160a01b0319166001600160a01b0393909316929092179091556004556100fb565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100d5578182fd5b82516001600160a01b03811681146100eb578283fd5b6020939093015192949293505050565b6108e08061010a6000396000f3fe60806040526004361061009c5760003560e01c8063715018a611610064578063715018a61461014c5780637799b7dc146101615780638da5cb5b14610174578063aa7b8aa81461019c578063d09e0cd8146101b2578063f2fde38b146101c857600080fd5b8063324870fd146100a1578063353b6c2c146100d4578063366aadfd146100f65780634cfea68a1461011657806354236b3b1461012c575b600080fd5b3480156100ad57600080fd5b506100c16100bc3660046106b2565b6101e8565b6040519081526020015b60405180910390f35b3480156100e057600080fd5b506100f46100ef3660046106b2565b610214565b005b34801561010257600080fd5b506100f46101113660046106b2565b61024c565b34801561012257600080fd5b506100c160045481565b34801561013857600080fd5b506100f46101473660046106b2565b61027b565b34801561015857600080fd5b506100f46102aa565b6100f461016f3660046106ca565b6102e0565b34801561018057600080fd5b506000546040516001600160a01b0390911681526020016100cb565b3480156101a857600080fd5b506100c160035481565b3480156101be57600080fd5b506100c160025481565b3480156101d457600080fd5b506100f46101e3366004610684565b610431565b60006101f26104cc565b61020483670de0b6b3a764000061089e565b61020e9190610790565b92915050565b6000546001600160a01b031633146102475760405162461bcd60e51b815260040161023e9061075b565b60405180910390fd5b600455565b6000546001600160a01b031633146102765760405162461bcd60e51b815260040161023e9061075b565b600355565b6000546001600160a01b031633146102a55760405162461bcd60e51b815260040161023e9061075b565b600255565b6000546001600160a01b031633146102d45760405162461bcd60e51b815260040161023e9061075b565b6102de6000610615565b565b60006102ec8284610790565b9050600060045460025483600354610304919061089e565b61030e9190610790565b6103189190610790565b90506000610325826101e8565b905080610333575050505050565b8034101561038d5760405162461bcd60e51b815260206004820152602160248201527f6e6f7420656e6f7567682045544820746f2070617920666f72207365727669636044820152606560f81b606482015260840161023e565b600080546040516001600160a01b039091169034908381818185875af1925050503d80600081146103da576040519150601f19603f3d011682016040523d82523d6000602084013e6103df565b606091505b50509050806104295760405162461bcd60e51b815260206004820152601660248201527518dbdd5b19081b9bdd081c185e48199bdc881c1bdbdb60521b604482015260640161023e565b505050505050565b6000546001600160a01b0316331461045b5760405162461bcd60e51b815260040161023e9061075b565b6001600160a01b0381166104c05760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161023e565b6104c981610615565b50565b600080600160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561051d57600080fd5b505afa158015610531573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610555919061073a565b90506000600160009054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b1580156105a757600080fd5b505afa1580156105bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105df91906106eb565b50505091505081600a6105f291906107f3565b61060490670de0b6b3a7640000610790565b61060e908261089e565b9250505090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805169ffffffffffffffffffff8116811461067f57600080fd5b919050565b600060208284031215610695578081fd5b81356001600160a01b03811681146106ab578182fd5b9392505050565b6000602082840312156106c3578081fd5b5035919050565b600080604083850312156106dc578081fd5b50508035926020909101359150565b600080600080600060a08688031215610702578081fd5b61070b86610665565b945060208601519350604086015192506060860151915061072e60808701610665565b90509295509295909350565b60006020828403121561074b578081fd5b815160ff811681146106ab578182fd5b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6000826107ab57634e487b7160e01b81526012600452602481fd5b500490565b600181815b808511156107eb5781600019048211156107d1576107d16108bd565b808516156107de57918102915b93841c93908002906107b5565b509250929050565b60006106ab60ff84168360008261080c5750600161020e565b816108195750600061020e565b816001811461082f576002811461083957610855565b600191505061020e565b60ff84111561084a5761084a6108bd565b50506001821b61020e565b5060208310610133831016604e8410600b8410161715610878575081810a61020e565b61088283836107b0565b8060001904821115610896576108966108bd565b029392505050565b60008160001904831182151516156108b8576108b86108bd565b500290565b634e487b7160e01b600052601160045260246000fdfea164736f6c6343000804000a000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000d9b6f6e53c60802d278efe0c643d9c01bbd93abc000000000000000000000000639fe6ab55c921f74e7fac1ee960c0b6293ba6120000000000000000000000000000000000000000000000000000000000001932

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

000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000d9b6f6e53c60802d278efe0c643d9c01bbd93abc000000000000000000000000639fe6ab55c921f74e7fac1ee960c0b6293ba6120000000000000000000000000000000000000000000000000000000000001932

-----Decoded View---------------
Arg [0] : _tokenAddress (address): 0x000000000000000000000000000000000000dead
Arg [1] : _spendAddress (address): 0xd9b6f6e53c60802d278efe0c643d9c01bbd93abc
Arg [2] : _linkPriceFeedContract (address): 0x639fe6ab55c921f74e7fac1ee960c0b6293ba612
Arg [3] : _blocksPerDay (uint256): 6450

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000dead
Arg [1] : 000000000000000000000000d9b6f6e53c60802d278efe0c643d9c01bbd93abc
Arg [2] : 000000000000000000000000639fe6ab55c921f74e7fac1ee960c0b6293ba612
Arg [3] : 0000000000000000000000000000000000000000000000000000000000001932


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.