ETH Price: $3,107.60 (-0.64%)
Gas: 2 Gwei

Contract

0xaf1610D242c7CdD30c546844aF75c147C12e94F9
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x6080604084854742019-09-04 19:29:491718 days ago1567625389IN
 Create: Pool
0 ETH0.0860229521.101

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Pool

Compiler Version
v0.5.10+commit.5a6ea5b1

Optimization Enabled:
No with 200 runs

Other Settings:
constantinople EvmVersion, GNU GPLv3 license
File 1 of 20 : Pool.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/access/Roles.sol";
import "./compound/ICErc20.sol";
import "./DrawManager.sol";
import "fixidity/contracts/FixidityLib.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";

/**
 * @title The Pool contract
 * @author Brendan Asselstine
 * @notice This contract allows users to pool deposits into Compound and win the accrued interest in periodic draws.
 * Funds are immediately deposited and withdrawn from the Compound cToken contract.
 * Draws go through three stages: open, committed and rewarded in that order.
 * Only one draw is ever in the open stage.  Users deposits are always added to the open draw.  Funds in the open Draw are that user's open balance.
 * When a Draw is committed, the funds in it are moved to a user's committed total and the total committed balance of all users is updated.
 * When a Draw is rewarded, the gross winnings are the accrued interest since the last reward (if any).  A winner is selected with their chances being
 * proportional to their committed balance vs the total committed balance of all users.
 *
 *
 * With the above in mind, there is always an open draw and possibly a committed draw.  The progression is:
 *
 * Step 1: Draw 1 Open
 * Step 2: Draw 2 Open | Draw 1 Committed
 * Step 3: Draw 3 Open | Draw 2 Committed | Draw 1 Rewarded
 * Step 4: Draw 4 Open | Draw 3 Committed | Draw 2 Rewarded
 * Step 5: Draw 5 Open | Draw 4 Committed | Draw 3 Rewarded
 * Step X: ...
 */
contract Pool is Initializable, ReentrancyGuard {
  using DrawManager for DrawManager.State;
  using SafeMath for uint256;
  using Roles for Roles.Role;

  uint256 private constant ETHER_IN_WEI = 1000000000000000000;

  /**
   * Emitted when a user deposits into the Pool.
   * @param sender The purchaser of the tickets
   * @param amount The size of the deposit
   */
  event Deposited(address indexed sender, uint256 amount);

  /**
   * Emitted when Sponsors have deposited into the Pool
   * @param sender The purchaser of the tickets
   * @param amount The size of the deposit
   */
  event SponsorshipDeposited(address indexed sender, uint256 amount);

  /**
   * Emitted when an admin has been added to the Pool.
   * @param admin The admin that was added
   */
  event AdminAdded(address indexed admin);

  /**
   * Emitted when an admin has been removed from the Pool.
   * @param admin The admin that was removed
   */
  event AdminRemoved(address indexed admin);

  /**
   * Emitted when a user withdraws from the pool.
   * @param sender The user that is withdrawing from the pool
   * @param amount The amount that the user withdrew
   */
  event Withdrawn(address indexed sender, uint256 amount);

  /**
   * Emitted when a new draw is opened for deposit.
   * @param drawId The draw id
   * @param feeBeneficiary The fee beneficiary for this draw
   * @param secretHash The committed secret hash
   * @param feeFraction The fee fraction of the winnings to be given to the beneficiary
   */
  event Opened(
    uint256 indexed drawId,
    address indexed feeBeneficiary,
    bytes32 secretHash,
    uint256 feeFraction
  );

  /**
   * Emitted when a draw is committed.
   * @param drawId The draw id
   */
  event Committed(
    uint256 indexed drawId
  );

  /**
   * Emitted when a draw is rewarded.
   * @param drawId The draw id
   * @param winner The address of the winner
   * @param entropy The entropy used to select the winner
   * @param winnings The net winnings given to the winner
   * @param fee The fee being given to the draw beneficiary
   */
  event Rewarded(
    uint256 indexed drawId,
    address indexed winner,
    bytes32 entropy,
    uint256 winnings,
    uint256 fee
  );

  /**
   * Emitted when the fee fraction is changed.  Takes effect on the next draw.
   * @param feeFraction The next fee fraction encoded as a fixed point 18 decimal
   */
  event NextFeeFractionChanged(uint256 feeFraction);

  /**
   * Emitted when the next fee beneficiary changes.  Takes effect on the next draw.
   * @param feeBeneficiary The next fee beneficiary
   */
  event NextFeeBeneficiaryChanged(address indexed feeBeneficiary);

  /**
   * Emitted when an admin pauses the contract
   */
  event Paused(address indexed sender);

  /**
   * Emitted when an admin unpauses the contract
   */
  event Unpaused(address indexed sender);

  struct Draw {
    uint256 feeFraction; //fixed point 18
    address feeBeneficiary;
    uint256 openedBlock;
    bytes32 secretHash;
  }

  /**
   * The Compound cToken that this Pool is bound to.
   */
  ICErc20 public cToken;

  /**
   * The fee beneficiary to use for subsequent Draws.
   */
  address public nextFeeBeneficiary;

  /**
   * The fee fraction to use for subsequent Draws.
   */
  uint256 public nextFeeFraction;

  /**
   * The total of all balances
   */
  uint256 public accountedBalance;

  /**
   * The total deposits and winnings for each user.
   */
  mapping (address => uint256) balances;

  /**
   * A mapping of draw ids to Draw structures
   */
  mapping(uint256 => Draw) draws;

  /**
   * A structure that is used to manage the user's odds of winning.
   */
  DrawManager.State drawState;

  /**
   * A structure containing the administrators
   */
  Roles.Role admins;

  /**
   * Whether the contract is paused
   */
  bool public paused;

  /**
   * @notice Initializes a new Pool contract.
   * @param _owner The owner of the Pool.  They are able to change settings and are set as the owner of new lotteries.
   * @param _cToken The Compound Finance MoneyMarket contract to supply and withdraw tokens.
   * @param _feeFraction The fraction of the gross winnings that should be transferred to the owner as the fee.  Is a fixed point 18 number.
   * @param _feeBeneficiary The address that will receive the fee fraction
   */
  function init (
    address _owner,
    address _cToken,
    uint256 _feeFraction,
    address _feeBeneficiary
  ) public initializer {
    require(_owner != address(0), "owner cannot be the null address");
    require(_cToken != address(0), "money market address is zero");
    cToken = ICErc20(_cToken);
    _addAdmin(_owner);
    _setNextFeeFraction(_feeFraction);
    _setNextFeeBeneficiary(_feeBeneficiary);
  }

  /**
   * @notice Opens a new Draw.
   * @param _secretHash The secret hash to commit to the Draw.
   */
  function open(bytes32 _secretHash) internal {
    drawState.openNextDraw();
    draws[drawState.openDrawIndex] = Draw(nextFeeFraction, nextFeeBeneficiary, block.number, _secretHash);
    emit Opened(
      drawState.openDrawIndex,
      nextFeeBeneficiary,
      _secretHash,
      nextFeeFraction
    );
  }

  /**
   * @notice Commits the current draw.
   */
  function commit() internal {
    uint256 drawId = currentOpenDrawId();
    emit Committed(drawId);
  }

  /**
   * @notice Commits the current open draw, if any, and opens the next draw using the passed hash.  Really this function is only called twice:
   * the first after Pool contract creation and the second immediately after.
   * Can only be called by an admin.
   * May fire the Committed event, and always fires the Open event.
   * @param nextSecretHash The secret hash to use to open a new Draw
   */
  function openNextDraw(bytes32 nextSecretHash) public onlyAdmin unlessPaused {
    require(currentCommittedDrawId() == 0, "there is a committed draw");
    if (currentOpenDrawId() != 0) {
      commit();
    }
    open(nextSecretHash);
  }

  /**
   * @notice Rewards the current committed draw using the passed secret, commits the current open draw, and opens the next draw using the passed secret hash.
   * Can only be called by an admin.
   * Fires the Rewarded event, the Committed event, and the Open event.
   * @param nextSecretHash The secret hash to use to open a new Draw
   * @param lastSecret The secret to reveal to reward the current committed Draw.
   */
  function rewardAndOpenNextDraw(bytes32 nextSecretHash, bytes32 lastSecret, bytes32 _salt) public onlyAdmin unlessPaused {
    require(currentCommittedDrawId() != 0, "a draw has not been committed");
    reward(lastSecret, _salt);
    commit();
    open(nextSecretHash);
  }

  /**
   * @notice Rewards the winner for the current committed Draw using the passed secret.
   * The gross winnings are calculated by subtracting the accounted balance from the current underlying cToken balance.
   * A winner is calculated using the revealed secret and a hash of the Draw's opened block and the gross winnings.
   * If there is a winner (i.e. any eligible users) then winner's balance is updated with their net winnings.
   * The draw beneficiary's balance is updated with the fee.
   * The accounted balance is updated to include the fee and, if there was a winner, the net winnings.
   * Fires the Rewarded event.
   * @param _secret The secret to reveal for the current committed Draw
   */
  function reward(bytes32 _secret, bytes32 _salt) internal {
    uint256 drawId = currentCommittedDrawId();
    Draw storage draw = draws[drawId];

    require(draw.secretHash == keccak256(abi.encodePacked(_secret, _salt)), "secret does not match");

    // Calculate the gross winnings
    uint256 underlyingBalance = balance();
    uint256 grossWinnings = underlyingBalance.sub(accountedBalance);

    // derive entropy from the revealed secret and the hash of the openedBlock and gross winnings
    bytes32 entropy = _secret ^ keccak256(abi.encodePacked(draw.openedBlock, grossWinnings));

    // Select the winner using the hash as entropy
    address winningAddress = calculateWinner(entropy);

    // Calculate the beneficiary fee
    uint256 fee = calculateFee(draw.feeFraction, grossWinnings);

    // Update balance of the beneficiary
    balances[draw.feeBeneficiary] = balances[draw.feeBeneficiary].add(fee);

    // Calculate the net winnings
    uint256 netWinnings = grossWinnings.sub(fee);

    // If there is a winner who is to receive non-zero winnings
    if (winningAddress != address(0) && netWinnings != 0) {
      // Update balance of the winner
      balances[winningAddress] = balances[winningAddress].add(netWinnings);

      // Enter their winnings into the next draw
      drawState.deposit(winningAddress, netWinnings);

      // Updated the accounted total
      accountedBalance = underlyingBalance;
    } else {
      // Only account for the fee
      accountedBalance = accountedBalance.add(fee);
    }

    // Destroy the draw now that it's complete
    delete draws[drawId];

    emit Rewarded(
      drawId,
      winningAddress,
      entropy,
      netWinnings,
      fee
    );
  }

  /**
   * @notice Calculate the beneficiary fee using the passed fee fraction and gross winnings.
   * @param _feeFraction The fee fraction, between 0 and 1, represented as a 18 point fixed number.
   * @param _grossWinnings The gross winnings to take a fraction of.
   */
  function calculateFee(uint256 _feeFraction, uint256 _grossWinnings) internal pure returns (uint256) {
    int256 grossWinningsFixed = FixidityLib.newFixed(int256(_grossWinnings));
    int256 feeFixed = FixidityLib.multiply(grossWinningsFixed, FixidityLib.newFixed(int256(_feeFraction), uint8(18)));
    return uint256(FixidityLib.fromFixed(feeFixed));
  }

  /**
   * @notice Allows a user to deposit a sponsorship amount.  The deposit is transferred into the cToken.
   * Sponsorships allow a user to contribute to the pool without becoming eligible to win.  They can withdraw their sponsorship at any time.
   * The deposit will immediately be added to Compound and the interest will contribute to the next draw.
   * @param _amount The amount of the token underlying the cToken to deposit.
   */
  function depositSponsorship(uint256 _amount) public nonReentrant unlessPaused {
    // Deposit the funds
    _deposit(_amount);

    emit SponsorshipDeposited(msg.sender, _amount);
  }

  /**
   * @notice Deposits into the pool under the current open Draw.  The deposit is transferred into the cToken.
   * Once the open draw is committed, the deposit will be added to the user's total committed balance and increase their chances of winning
   * proportional to the total committed balance of all users.
   * @param _amount The amount of the token underlying the cToken to deposit.
   */
  function depositPool(uint256 _amount) public requireOpenDraw nonReentrant unlessPaused {
    // Update the user's eligibility
    drawState.deposit(msg.sender, _amount);

    // Deposit the funds
    _deposit(_amount);

    emit Deposited(msg.sender, _amount);
  }

  /**
   * @notice Transfers tokens from the sender into the Compound cToken contract and updates the accountedBalance.
   * @param _amount The amount of the token underlying the cToken to deposit.
   */
  function _deposit(uint256 _amount) internal {
    require(_amount > 0, "deposit is not greater than zero");

    // Transfer the tokens into this contract
    require(token().transferFrom(msg.sender, address(this), _amount), "token transfer failed");

    // Update the user's balance
    balances[msg.sender] = balances[msg.sender].add(_amount);

    // Update the total of this contract
    accountedBalance = accountedBalance.add(_amount);

    // Deposit into Compound
    require(token().approve(address(cToken), _amount), "could not approve money market spend");
    require(cToken.mint(_amount) == 0, "could not supply money market");
  }

  /**
   * @notice Withdraw the sender's entire balance back to them.
   */
  function withdraw() public nonReentrant {
    uint balance = balances[msg.sender];

    require(balance > 0, "balance has already been withdrawn");

    // Update the user's balance
    balances[msg.sender] = 0;

    // Update their chances of winning
    drawState.withdraw(msg.sender);

    _withdraw(balance);
  }

  /**
   * @notice Transfers tokens from the cToken contract to the sender.  Updates the accounted balance.
   */
  function _withdraw(uint256 _amount) internal {
    require(_amount > 0, "withdrawal is not greater than zero");

    // Update the total of this contract
    accountedBalance = accountedBalance.sub(_amount);

    // Withdraw from Compound and transfer
    require(cToken.redeemUnderlying(_amount) == 0, "could not redeem from compound");
    require(token().transfer(msg.sender, _amount), "could not transfer winnings");

    emit Withdrawn(msg.sender, _amount);
  }

  /**
   * @notice Returns the id of the current open Draw.
   * @return The current open Draw id
   */
  function currentOpenDrawId() public view returns (uint256) {
    return drawState.openDrawIndex;
  }

  /**
   * @notice Returns the id of the current committed Draw.
   * @return The current committed Draw id
   */
  function currentCommittedDrawId() public view returns (uint256) {
    if (drawState.openDrawIndex > 1) {
      return drawState.openDrawIndex - 1;
    } else {
      return 0;
    }
  }

  /**
   * @notice Gets information for a given draw.
   * @param _drawId The id of the Draw to retrieve info for.
   * @return Fields including:
   *  feeFraction: the fee fraction
   *  feeBeneficiary: the beneficiary of the fee
   *  openedBlock: The block at which the draw was opened
   *  secretHash: The hash of the secret committed to this draw.
   */
  function getDraw(uint256 _drawId) public view returns (
    uint256 feeFraction,
    address feeBeneficiary,
    uint256 openedBlock,
    bytes32 secretHash
  ) {
    Draw storage draw = draws[_drawId];
    feeFraction = draw.feeFraction;
    feeBeneficiary = draw.feeBeneficiary;
    openedBlock = draw.openedBlock;
    secretHash = draw.secretHash;
  }

  /**
   * @notice Returns the total of the address's balance in committed Draws.  That is, the total that contributes to their chances of winning.
   * @param _addr The address of the user
   * @return The total committed balance for the user
   */
  function committedBalanceOf(address _addr) public view returns (uint256) {
    return drawState.committedBalanceOf(_addr);
  }

  /**
   * @notice Returns the total of the address's balance in the open Draw.  That is, the total that will *eventually* contribute to their chances of winning.
   * @param _addr The address of the user
   * @return The total open balance for the user
   */
  function openBalanceOf(address _addr) public view returns (uint256) {
    return drawState.openBalanceOf(_addr);
  }

  /**
   * @notice Returns a user's total balance, including both committed Draw balance and open Draw balance.
   * @param _addr The address of the user to check.
   * @return The users's current balance.
   */
  function balanceOf(address _addr) public view returns (uint256) {
    return balances[_addr];
  }

  /**
   * @notice Calculates a winner using the passed entropy for the current committed balances.
   * @param _entropy The entropy to use to select the winner
   * @return The winning address
   */
  function calculateWinner(bytes32 _entropy) public view returns (address) {
    return drawState.drawWithEntropy(_entropy);
  }

  /**
   * @notice Returns the total committed balance.  Used to compute an address's chances of winning.
   * @return The total committed balance.
   */
  function committedSupply() public view returns (uint256) {
    return drawState.committedSupply;
  }

  /**
   * @notice Returns the total open balance.  This balance is the number of tickets purchased for the open draw.
   * @return The total open balance
   */
  function openSupply() public view returns (uint256) {
    return drawState.openSupply();
  }

  /**
   * @notice Calculates the total estimated interest earned for the given number of blocks
   * @param _blocks The number of block that interest accrued for
   * @return The total estimated interest as a 18 point fixed decimal.
   */
  function estimatedInterestRate(uint256 _blocks) public view returns (uint256) {
    return supplyRatePerBlock().mul(_blocks);
  }

  /**
   * @notice Convenience function to return the supplyRatePerBlock value from the money market contract.
   * @return The cToken supply rate per block
   */
  function supplyRatePerBlock() public view returns (uint256) {
    return cToken.supplyRatePerBlock();
  }

  /**
   * @notice Sets the beneficiary fee fraction for subsequent Draws.
   * Fires the NextFeeFractionChanged event.
   * Can only be called by an admin.
   * @param _feeFraction The fee fraction to use.
   * Must be between 0 and 1 and formatted as a fixed point number with 18 decimals (as in Ether).
   */
  function setNextFeeFraction(uint256 _feeFraction) public onlyAdmin {
    _setNextFeeFraction(_feeFraction);
  }

  function _setNextFeeFraction(uint256 _feeFraction) internal {
    require(_feeFraction >= 0, "fee must be zero or greater");
    require(_feeFraction <= ETHER_IN_WEI, "fee fraction must be 1 or less");
    nextFeeFraction = _feeFraction;

    emit NextFeeFractionChanged(_feeFraction);
  }

  /**
   * @notice Sets the fee beneficiary for subsequent Draws.
   * Can only be called by admins.
   * @param _feeBeneficiary The beneficiary for the fee fraction.  Cannot be the 0 address.
   */
  function setNextFeeBeneficiary(address _feeBeneficiary) public onlyAdmin {
    _setNextFeeBeneficiary(_feeBeneficiary);
  }

  function _setNextFeeBeneficiary(address _feeBeneficiary) internal {
    require(_feeBeneficiary != address(0), "beneficiary should not be 0x0");
    nextFeeBeneficiary = _feeBeneficiary;

    emit NextFeeBeneficiaryChanged(_feeBeneficiary);
  }

  /**
   * @notice Adds an administrator.
   * Can only be called by administrators.
   * Fires the AdminAdded event.
   * @param _admin The address of the admin to add
   */
  function addAdmin(address _admin) public onlyAdmin {
    _addAdmin(_admin);
  }

  /**
   * @notice Checks whether a given address is an administrator.
   * @param _admin The address to check
   * @return True if the address is an admin, false otherwise.
   */
  function isAdmin(address _admin) public view returns (bool) {
    return admins.has(_admin);
  }

  function _addAdmin(address _admin) internal {
    admins.add(_admin);

    emit AdminAdded(_admin);
  }

  /**
   * @notice Removes an administrator
   * Can only be called by an admin.
   * @param _admin The address of the admin to remove
   */
  function removeAdmin(address _admin) public onlyAdmin {
    require(admins.has(_admin), "admin does not exist");
    admins.remove(_admin);

    emit AdminRemoved(_admin);
  }

  /**
   * @notice Returns the token underlying the cToken.
   * @return An ERC20 token address
   */
  function token() internal view returns (IERC20) {
    return IERC20(cToken.underlying());
  }

  /**
   * @notice Returns the underlying balance of this contract in the cToken.
   * @return The cToken underlying balance for this contract.
   */
  function balance() public returns (uint256) {
    return cToken.balanceOfUnderlying(address(this));
  }

  function pause() public unlessPaused onlyAdmin {
    paused = true;

    emit Paused(msg.sender);
  }

  function unpause() public whenPaused onlyAdmin {
    paused = false;

    emit Unpaused(msg.sender);
  }

  modifier onlyAdmin() {
    require(admins.has(msg.sender), "must be an admin");
    _;
  }

  modifier requireOpenDraw() {
    require(currentOpenDrawId() != 0, "there is no open draw");
    _;
  }

  modifier whenPaused() {
    require(paused, "contract is not paused");
    _;
  }

  modifier unlessPaused() {
    require(!paused, "contract is paused");
    _;
  }
}

File 2 of 20 : DrawManager.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

import "./UniformRandomNumber.sol";
import "@kleros/kleros/contracts/data-structures/SortitionSumTreeFactory.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";

/**
 * @author Brendan Asselstine
 * @notice Tracks committed and open balances for addresses.  Affords selection of an address by indexing all committed balances.
 *
 * Balances are tracked in Draws.  There is always one open Draw.  Deposits are always added to the open Draw.
 * When a new draw is opened, the previous opened draw is committed.
 *
 * The committed balance for an address is the total of their balances for committed Draws.
 * An address's open balance is their balance in the open Draw.
 */
library DrawManager {
    using SortitionSumTreeFactory for SortitionSumTreeFactory.SortitionSumTrees;
    using SafeMath for uint256;

    /**
     * The ID to use for the selection tree.
     */
    bytes32 public constant TREE_OF_DRAWS = "TreeOfDraws";

    uint8 public constant MAX_LEAVES = 10;

    /**
     * Stores information for all draws.
     */
    struct State {
        /**
         * Each Draw stores it's address balances in a sortitionSumTree.  Draw trees are indexed using the Draw index.
         * There is one root sortitionSumTree that stores all of the draw totals.  The root tree is indexed using the constant TREE_OF_DRAWS.
         */
        SortitionSumTreeFactory.SortitionSumTrees sortitionSumTrees;

        /**
         * Stores the first Draw index that an address deposited to.
         */
        mapping(address => uint256) usersFirstDrawIndex;

        /**
         * Stores the last Draw index that an address deposited to.
         */
        mapping(address => uint256) usersSecondDrawIndex;

        /**
         * Stores a mapping of Draw index => Draw total
         */
        mapping(uint256 => uint256) drawTotals;

        /**
         * The current open Draw index
         */
        uint256 openDrawIndex;

        /**
         * The total of committed balances
         */
        uint256 committedSupply;
    }

    /**
     * @notice Opens the next Draw and commits the previous open Draw (if any).
     * @param self The drawState this library is attached to
     * @return The index of the new open Draw
     */
    function openNextDraw(State storage self) public returns (uint256) {
        if (self.openDrawIndex == 0) {
            // If there is no previous draw, we must initialize
            self.sortitionSumTrees.createTree(TREE_OF_DRAWS, MAX_LEAVES);
        } else {
            // else add current draw to sortition sum trees
            bytes32 drawId = bytes32(self.openDrawIndex);
            uint256 drawTotal = self.drawTotals[self.openDrawIndex];
            self.sortitionSumTrees.set(TREE_OF_DRAWS, drawTotal, drawId);
            self.committedSupply = self.committedSupply.add(drawTotal);
        }
        // now create a new draw
        uint256 drawIndex = self.openDrawIndex.add(1);
        self.sortitionSumTrees.createTree(bytes32(drawIndex), MAX_LEAVES);
        self.openDrawIndex = drawIndex;

        return drawIndex;
    }

    /**
     * @notice Deposits the given amount into the current open draw by the given user.
     * @param self The DrawManager state
     * @param _addr The address to deposit for
     * @param _amount The amount to deposit
     */
    function deposit(State storage self, address _addr, uint256 _amount) public requireOpenDraw(self) {
        bytes32 userId = bytes32(uint256(_addr));
        uint256 openDrawIndex = self.openDrawIndex;

        // update the current draw
        uint256 currentAmount = self.sortitionSumTrees.stakeOf(bytes32(openDrawIndex), userId);
        currentAmount = currentAmount.add(_amount);
        drawSet(self, openDrawIndex, currentAmount, _addr);

        uint256 firstDrawIndex = self.usersFirstDrawIndex[_addr];
        uint256 secondDrawIndex = self.usersSecondDrawIndex[_addr];

        // if this is the users first draw, set it
        if (firstDrawIndex == 0) {
            self.usersFirstDrawIndex[_addr] = openDrawIndex;
        // otherwise, if the first draw is not this draw
        } else if (firstDrawIndex != openDrawIndex) {
            // if a second draw does not exist
            if (secondDrawIndex == 0) {
                // set the second draw to the current draw
                self.usersSecondDrawIndex[_addr] = openDrawIndex;
            // otherwise if a second draw exists but is not the current one
            } else if (secondDrawIndex != openDrawIndex) {
                // merge it into the first draw, and update the second draw index to this one
                uint256 firstAmount = self.sortitionSumTrees.stakeOf(bytes32(firstDrawIndex), userId);
                uint256 secondAmount = self.sortitionSumTrees.stakeOf(bytes32(secondDrawIndex), userId);
                drawSet(self, firstDrawIndex, firstAmount.add(secondAmount), _addr);
                drawSet(self, secondDrawIndex, 0, _addr);
                self.usersSecondDrawIndex[_addr] = openDrawIndex;
            }
        }
    }

    /**
     * @notice Withdraws a user's committed and open draws.
     * @param self The DrawManager state
     * @param _addr The address whose balance to withdraw
     */
    function withdraw(State storage self, address _addr) public requireOpenDraw(self) {
        uint256 firstDrawIndex = self.usersFirstDrawIndex[_addr];
        uint256 secondDrawIndex = self.usersSecondDrawIndex[_addr];

        if (firstDrawIndex != 0) {
            drawSet(self, firstDrawIndex, 0, _addr);
        }

        if (secondDrawIndex != 0) {
            drawSet(self, secondDrawIndex, 0, _addr);
        }
    }

    /**
     * @notice Returns the total balance for an address, including committed balances and the open balance.
     */
    function balanceOf(State storage drawState, address _addr) public view returns (uint256) {
        return committedBalanceOf(drawState, _addr).add(openBalanceOf(drawState, _addr));
    }

    /**
     * @notice Returns the total committed balance for an address.
     * @param self The DrawManager state
     * @param _addr The address whose committed balance should be returned
     * @return The total committed balance
     */
    function committedBalanceOf(State storage self, address _addr) public view returns (uint256) {
        uint256 balance = 0;

        uint256 firstDrawIndex = self.usersFirstDrawIndex[_addr];
        uint256 secondDrawIndex = self.usersSecondDrawIndex[_addr];

        if (firstDrawIndex != 0 && firstDrawIndex != self.openDrawIndex) {
            balance = balance.add(self.sortitionSumTrees.stakeOf(bytes32(firstDrawIndex), bytes32(uint256(_addr))));
        }

        if (secondDrawIndex != 0 && secondDrawIndex != self.openDrawIndex) {
            balance = balance.add(self.sortitionSumTrees.stakeOf(bytes32(secondDrawIndex), bytes32(uint256(_addr))));
        }

        return balance;
    }

    /**
     * @notice Returns the open balance for an address
     * @param self The DrawManager state
     * @param _addr The address whose open balance should be returned
     * @return The open balance
     */
    function openBalanceOf(State storage self, address _addr) public view returns (uint256) {
        if (self.openDrawIndex == 0) {
            return 0;
        } else {
            return self.sortitionSumTrees.stakeOf(bytes32(self.openDrawIndex), bytes32(uint256(_addr)));
        }
    }

    /**
     * @notice Returns the open Draw balance for the DrawManager
     * @param self The DrawManager state
     * @return The open draw total balance
     */
    function openSupply(State storage self) public view returns (uint256) {
        return self.drawTotals[self.openDrawIndex];
    }

    /**
     * @notice Updates the Draw balance for an address.
     * @param self The DrawManager state
     * @param _drawIndex The Draw index
     * @param _amount The new balance
     * @param _addr The address whose balance should be updated
     */
    function drawSet(State storage self, uint256 _drawIndex, uint256 _amount, address _addr) internal {
        bytes32 drawId = bytes32(_drawIndex);
        bytes32 userId = bytes32(uint256(_addr));
        uint256 oldAmount = self.sortitionSumTrees.stakeOf(drawId, userId);

        if (oldAmount != _amount) {
            // If the amount has changed

            // Update the Draw's balance for that address
            self.sortitionSumTrees.set(drawId, _amount, userId);

            uint256 drawTotal = self.drawTotals[_drawIndex];
            if (oldAmount > _amount) {
                // If the amount is less than the old amount

                // Subtract the difference from the Draw total
                uint256 diffAmount = oldAmount.sub(_amount);
                drawTotal = drawTotal.sub(diffAmount);
                if (_drawIndex != self.openDrawIndex) {
                    // If the Draw is committed, update the root tree and committed supply
                    self.sortitionSumTrees.set(TREE_OF_DRAWS, drawTotal, drawId);
                    self.committedSupply = self.committedSupply.sub(diffAmount);
                }
            } else { // oldAmount < _amount
                // if the amount is greater than the old amount

                // Add the difference to the Draw total
                uint256 diffAmount = _amount.sub(oldAmount);
                drawTotal = drawTotal.add(diffAmount);
                if (_drawIndex != self.openDrawIndex) {
                    // If the Draw is committed, update the root tree and committed supply
                    self.sortitionSumTrees.set(TREE_OF_DRAWS, drawTotal, drawId);
                    self.committedSupply = self.committedSupply.add(diffAmount);
                }
            }
            // Update the Draw total with the new total
            self.drawTotals[_drawIndex] = drawTotal;
        }
    }

   /**
     * @notice Selects an address by indexing into the committed tokens using the passed token
     * @param self The DrawManager state
     * @param _token The token index to select
     * @return The selected address
     */
    function draw(State storage self, uint256 _token) public view returns (address) {
        // If there is no one to select, just return the zero address
        if (self.committedSupply == 0) {
            return address(0);
        }
        require(_token < self.committedSupply, "token is beyond the eligible supply");
        uint256 drawIndex = uint256(self.sortitionSumTrees.draw(TREE_OF_DRAWS, _token));
        assert(drawIndex != 0);
        uint256 drawSupply = self.drawTotals[drawIndex];
        assert(drawSupply > 0);
        uint256 drawToken = _token % drawSupply;
        return address(uint256(self.sortitionSumTrees.draw(bytes32(drawIndex), drawToken)));
    }

    /**
     * @notice Selects an address using the entropy as an index into the committed tokens
     * The entropy is passed into the UniformRandomNumber library to remove modulo bias.
     * @param self The DrawManager state
     * @param _entropy The random entropy to use
     * @return The selected address
     */
    function drawWithEntropy(State storage self, bytes32 _entropy) public view returns (address) {
        return draw(self, UniformRandomNumber.uniform(uint256(_entropy), self.committedSupply));
    }

    modifier requireOpenDraw(State storage self) {
        require(self.openDrawIndex > 0, "there is no open draw");
        _;
    }
}

File 3 of 20 : Migrations.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

// Migrations.sol

pragma solidity 0.5.10;

import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";

contract Migrations is Ownable {
  uint public last_completed_migration;

  function setCompleted(uint completed) public onlyOwner {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) public onlyOwner {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

File 5 of 20 : UniformRandomNumber.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

/**
 * @author Brendan Asselstine
 * @notice A library that uses entropy to select a random number within a bound.  Compensates for modulo bias.
 * @dev Thanks to https://medium.com/hownetworks/dont-waste-cycles-with-modulo-bias-35b6fdafcf94
 */
library UniformRandomNumber {
  /// @notice Select a random number without modulo bias using a random seed and upper bound
  /// @param _entropy The seed for randomness
  /// @param _upperBound The upper bound of the desired number
  /// @return A random number less than the _upperBound
  function uniform(uint256 _entropy, uint256 _upperBound) internal pure returns (uint256) {
    if (_upperBound == 0) {
      return 0;
    }
    uint256 min = -_upperBound % _upperBound;
    uint256 random = _entropy;
    while (true) {
      if (random >= min) {
        break;
      }
      random = uint256(keccak256(abi.encodePacked(random)));
    }
    return random % _upperBound;
  }
}

File 6 of 20 : ICErc20.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

contract ICErc20 {
    address public underlying;
    function mint(uint mintAmount) external returns (uint);
    function redeemUnderlying(uint redeemAmount) external returns (uint);
    function balanceOfUnderlying(address owner) external returns (uint);
    function getCash() external view returns (uint);
    function supplyRatePerBlock() external view returns (uint);
}

File 7 of 20 : CErc20Mock.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

import "../compound/ICErc20.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";

contract CErc20Mock is Initializable, ICErc20 {
  mapping(address => uint256) ownerTokenAmounts;

  uint __supplyRatePerBlock;

  function initialize (address _token, uint256 _supplyRatePerBlock) public initializer {
    require(_token != address(0), "token is not defined");
    underlying = _token;
    __supplyRatePerBlock = _supplyRatePerBlock;
  }

  function mint(uint amount) external returns (uint) {
    ownerTokenAmounts[msg.sender] = ownerTokenAmounts[msg.sender] + amount;
    require(IERC20(underlying).transferFrom(msg.sender, address(this), amount), "could not transfer tokens");
    return 0;
  }

  function getCash() external view returns (uint) {
    return IERC20(underlying).balanceOf(address(this));
  }

  function redeemUnderlying(uint requestedAmount) external returns (uint) {
    require(ownerTokenAmounts[msg.sender] > 0, "you must have supplied tokens");
    ownerTokenAmounts[msg.sender] = ownerTokenAmounts[msg.sender] - requestedAmount;
    require(IERC20(underlying).transfer(msg.sender, requestedAmount), "could not transfer tokens");
  }

  function reward(address account) external {
    ownerTokenAmounts[account] = (ownerTokenAmounts[account] * 120) / 100;
  }

  function balanceOfUnderlying(address account) external returns (uint) {
    return ownerTokenAmounts[account];
  }

  function supplyRatePerBlock() external view returns (uint) {
    return __supplyRatePerBlock;
  }

  function setSupplyRateMantissa(uint256 _supplyRatePerBlock) external {
    __supplyRatePerBlock = _supplyRatePerBlock;
  }
}

File 8 of 20 : ExposedDrawManager.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

import "../DrawManager.sol";

contract ExposedDrawManager {
    using DrawManager for DrawManager.State;

    DrawManager.State state;

    function openNextDraw() public {
      state.openNextDraw();
    }

    function deposit(address user, uint256 amount) public {
      state.deposit(user, amount);
    }

    function withdraw(address user) public {
      state.withdraw(user);
    }

    function balanceOf(address user) public view returns (uint256) {
      return state.balanceOf(user);
    }

    function committedBalanceOf(address user) public view returns (uint256) {
      return state.committedBalanceOf(user);
    }

    function openBalanceOf(address user) public view returns (uint256) {
      return state.openBalanceOf(user);
    }

    function committedSupply() public view returns (uint256) {
      return state.committedSupply;
    }

    function openSupply() public view returns (uint256) {
      return state.openSupply();
    }

    function openDrawIndex() public view returns (uint256) {
      return state.openDrawIndex;
    }

    function draw(uint256 token) public view returns (address) {
      return state.draw(token);
    }

    function firstDrawIndex(address user) public view returns (uint256) {
        return state.usersFirstDrawIndex[user];
    }

    function secondDrawIndex(address user) public view returns (uint256) {
        return state.usersSecondDrawIndex[user];
    }

    function drawWithEntropy(bytes32 entropy) public view returns (address) {
        return state.drawWithEntropy(entropy);
    }
}

File 9 of 20 : ExposedUniformRandomNumber.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity ^0.5.10;

import "../UniformRandomNumber.sol";

contract ExposedUniformRandomNumber {
  function uniform(uint256 _entropy, uint256 _upperBound) public pure returns (uint256) {
    return UniformRandomNumber.uniform(_entropy, _upperBound);
  }
}

File 10 of 20 : Token.sol
/**
Copyright 2019 PoolTogether LLC

This file is part of PoolTogether.

PoolTogether is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation under version 3 of the License.

PoolTogether is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PoolTogether.  If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.5.10;

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol";

/**
 * @author Brendan Asselstine
 * @notice An ERC20 Token contract to be used for testing the Pool contract
 */
contract Token is ERC20Mintable {
  string public constant name = "Token";
  string public constant symbol = "TOK";
  uint8 public constant decimals = 18;
}

File 11 of 20 : SortitionSumTreeFactory.sol
/**
 *  @reviewers: [@clesaege, @unknownunknown1, @ferittuncer]
 *  @auditors: []
 *  @bounties: [<14 days 10 ETH max payout>]
 *  @deployments: []
 */

pragma solidity ^0.5.0;

/**
 *  @title SortitionSumTreeFactory
 *  @author Enrique Piqueras - <[email protected]>
 *  @dev A factory of trees that keep track of staked values for sortition.
 */
library SortitionSumTreeFactory {
    /* Structs */

    struct SortitionSumTree {
        uint K; // The maximum number of childs per node.
        // We use this to keep track of vacant positions in the tree after removing a leaf. This is for keeping the tree as balanced as possible without spending gas on moving nodes around.
        uint[] stack;
        uint[] nodes;
        // Two-way mapping of IDs to node indexes. Note that node index 0 is reserved for the root node, and means the ID does not have a node.
        mapping(bytes32 => uint) IDsToNodeIndexes;
        mapping(uint => bytes32) nodeIndexesToIDs;
    }

    /* Storage */

    struct SortitionSumTrees {
        mapping(bytes32 => SortitionSumTree) sortitionSumTrees;
    }

    /* internal */

    /**
     *  @dev Create a sortition sum tree at the specified key.
     *  @param _key The key of the new tree.
     *  @param _K The number of children each node in the tree should have.
     */
    function createTree(SortitionSumTrees storage self, bytes32 _key, uint _K) internal {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];
        require(tree.K == 0, "Tree already exists.");
        require(_K > 1, "K must be greater than one.");
        tree.K = _K;
        tree.stack.length = 0;
        tree.nodes.length = 0;
        tree.nodes.push(0);
    }

    /**
     *  @dev Set a value of a tree.
     *  @param _key The key of the tree.
     *  @param _value The new value.
     *  @param _ID The ID of the value.
     *  `O(log_k(n))` where
     *  `k` is the maximum number of childs per node in the tree,
     *   and `n` is the maximum number of nodes ever appended.
     */
    function set(SortitionSumTrees storage self, bytes32 _key, uint _value, bytes32 _ID) internal {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];
        uint treeIndex = tree.IDsToNodeIndexes[_ID];

        if (treeIndex == 0) { // No existing node.
            if (_value != 0) { // Non zero value.
                // Append.
                // Add node.
                if (tree.stack.length == 0) { // No vacant spots.
                    // Get the index and append the value.
                    treeIndex = tree.nodes.length;
                    tree.nodes.push(_value);

                    // Potentially append a new node and make the parent a sum node.
                    if (treeIndex != 1 && (treeIndex - 1) % tree.K == 0) { // Is first child.
                        uint parentIndex = treeIndex / tree.K;
                        bytes32 parentID = tree.nodeIndexesToIDs[parentIndex];
                        uint newIndex = treeIndex + 1;
                        tree.nodes.push(tree.nodes[parentIndex]);
                        delete tree.nodeIndexesToIDs[parentIndex];
                        tree.IDsToNodeIndexes[parentID] = newIndex;
                        tree.nodeIndexesToIDs[newIndex] = parentID;
                    }
                } else { // Some vacant spot.
                    // Pop the stack and append the value.
                    treeIndex = tree.stack[tree.stack.length - 1];
                    tree.stack.length--;
                    tree.nodes[treeIndex] = _value;
                }

                // Add label.
                tree.IDsToNodeIndexes[_ID] = treeIndex;
                tree.nodeIndexesToIDs[treeIndex] = _ID;

                updateParents(self, _key, treeIndex, true, _value);
            }
        } else { // Existing node.
            if (_value == 0) { // Zero value.
                // Remove.
                // Remember value and set to 0.
                uint value = tree.nodes[treeIndex];
                tree.nodes[treeIndex] = 0;

                // Push to stack.
                tree.stack.push(treeIndex);

                // Clear label.
                delete tree.IDsToNodeIndexes[_ID];
                delete tree.nodeIndexesToIDs[treeIndex];

                updateParents(self, _key, treeIndex, false, value);
            } else if (_value != tree.nodes[treeIndex]) { // New, non zero value.
                // Set.
                bool plusOrMinus = tree.nodes[treeIndex] <= _value;
                uint plusOrMinusValue = plusOrMinus ? _value - tree.nodes[treeIndex] : tree.nodes[treeIndex] - _value;
                tree.nodes[treeIndex] = _value;

                updateParents(self, _key, treeIndex, plusOrMinus, plusOrMinusValue);
            }
        }
    }

    /* internal Views */

    /**
     *  @dev Query the leaves of a tree. Note that if `startIndex == 0`, the tree is empty and the root node will be returned.
     *  @param _key The key of the tree to get the leaves from.
     *  @param _cursor The pagination cursor.
     *  @param _count The number of items to return.
     *  @return The index at which leaves start, the values of the returned leaves, and whether there are more for pagination.
     *  `O(n)` where
     *  `n` is the maximum number of nodes ever appended.
     */
    function queryLeafs(
        SortitionSumTrees storage self,
        bytes32 _key,
        uint _cursor,
        uint _count
    ) internal view returns(uint startIndex, uint[] memory values, bool hasMore) {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];

        // Find the start index.
        for (uint i = 0; i < tree.nodes.length; i++) {
            if ((tree.K * i) + 1 >= tree.nodes.length) {
                startIndex = i;
                break;
            }
        }

        // Get the values.
        uint loopStartIndex = startIndex + _cursor;
        values = new uint[](loopStartIndex + _count > tree.nodes.length ? tree.nodes.length - loopStartIndex : _count);
        uint valuesIndex = 0;
        for (uint j = loopStartIndex; j < tree.nodes.length; j++) {
            if (valuesIndex < _count) {
                values[valuesIndex] = tree.nodes[j];
                valuesIndex++;
            } else {
                hasMore = true;
                break;
            }
        }
    }

    /**
     *  @dev Draw an ID from a tree using a number. Note that this function reverts if the sum of all values in the tree is 0.
     *  @param _key The key of the tree.
     *  @param _drawnNumber The drawn number.
     *  @return The drawn ID.
     *  `O(k * log_k(n))` where
     *  `k` is the maximum number of childs per node in the tree,
     *   and `n` is the maximum number of nodes ever appended.
     */
    function draw(SortitionSumTrees storage self, bytes32 _key, uint _drawnNumber) internal view returns(bytes32 ID) {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];
        uint treeIndex = 0;
        uint currentDrawnNumber = _drawnNumber % tree.nodes[0];

        while ((tree.K * treeIndex) + 1 < tree.nodes.length)  // While it still has children.
            for (uint i = 1; i <= tree.K; i++) { // Loop over children.
                uint nodeIndex = (tree.K * treeIndex) + i;
                uint nodeValue = tree.nodes[nodeIndex];

                if (currentDrawnNumber >= nodeValue) currentDrawnNumber -= nodeValue; // Go to the next child.
                else { // Pick this child.
                    treeIndex = nodeIndex;
                    break;
                }
            }
        
        ID = tree.nodeIndexesToIDs[treeIndex];
    }

    /** @dev Gets a specified ID's associated value.
     *  @param _key The key of the tree.
     *  @param _ID The ID of the value.
     *  @return The associated value.
     */
    function stakeOf(SortitionSumTrees storage self, bytes32 _key, bytes32 _ID) internal view returns(uint value) {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];
        uint treeIndex = tree.IDsToNodeIndexes[_ID];

        if (treeIndex == 0) value = 0;
        else value = tree.nodes[treeIndex];
    }

    /* Private */

    /**
     *  @dev Update all the parents of a node.
     *  @param _key The key of the tree to update.
     *  @param _treeIndex The index of the node to start from.
     *  @param _plusOrMinus Wether to add (true) or substract (false).
     *  @param _value The value to add or substract.
     *  `O(log_k(n))` where
     *  `k` is the maximum number of childs per node in the tree,
     *   and `n` is the maximum number of nodes ever appended.
     */
    function updateParents(SortitionSumTrees storage self, bytes32 _key, uint _treeIndex, bool _plusOrMinus, uint _value) private {
        SortitionSumTree storage tree = self.sortitionSumTrees[_key];

        uint parentIndex = _treeIndex;
        while (parentIndex != 0) {
            parentIndex = (parentIndex - 1) / tree.K;
            tree.nodes[parentIndex] = _plusOrMinus ? tree.nodes[parentIndex] + _value : tree.nodes[parentIndex] - _value;
        }
    }
}

File 12 of 20 : SafeMath.sol
pragma solidity ^0.5.2;

/**
 * @title SafeMath
 * @dev Unsigned math operations with safety checks that revert on error
 */
library SafeMath {
    /**
     * @dev Multiplies two unsigned integers, reverts on overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b);

        return c;
    }

    /**
     * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Adds two unsigned integers, reverts on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a);

        return c;
    }

    /**
     * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
     * reverts when dividing by zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}

File 13 of 20 : Ownable.sol
pragma solidity ^0.5.2;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable is Initializable {
    address private _owner;

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

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    function initialize(address sender) public initializer {
        _owner = sender;
        emit OwnershipTransferred(address(0), _owner);
    }

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

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

    /**
     * @return true if `msg.sender` is the owner of the contract.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Allows the current owner to relinquish control of the contract.
     * It will not be possible to call the functions with the `onlyOwner`
     * modifier anymore.
     * @notice Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0));
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

    uint256[50] private ______gap;
}

File 14 of 20 : IERC20.sol
pragma solidity ^0.5.2;

/**
 * @title ERC20 interface
 * @dev see https://eips.ethereum.org/EIPS/eip-20
 */
interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

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

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 15 of 20 : ReentrancyGuard.sol
pragma solidity ^0.5.2;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

/**
 * @title Helps contracts guard against reentrancy attacks.
 * @author Remco Bloemen <remco@2π.com>, Eenae <[email protected]>
 * @dev If you mark a function `nonReentrant`, you should also
 * mark it `external`.
 */
contract ReentrancyGuard is Initializable {
    /// @dev counter to allow mutex lock with only one SSTORE operation
    uint256 private _guardCounter;

    function initialize() public initializer {
        // The counter starts at one to prevent changing it from zero to a non-zero
        // value, which is a more expensive operation.
        _guardCounter = 1;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _guardCounter += 1;
        uint256 localCounter = _guardCounter;
        _;
        require(localCounter == _guardCounter);
    }

    uint256[50] private ______gap;
}

File 16 of 20 : Roles.sol
pragma solidity ^0.5.2;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
    struct Role {
        mapping (address => bool) bearer;
    }

    /**
     * @dev give an account access to this role
     */
    function add(Role storage role, address account) internal {
        require(account != address(0));
        require(!has(role, account));

        role.bearer[account] = true;
    }

    /**
     * @dev remove an account's access to this role
     */
    function remove(Role storage role, address account) internal {
        require(account != address(0));
        require(has(role, account));

        role.bearer[account] = false;
    }

    /**
     * @dev check if an account has this role
     * @return bool
     */
    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0));
        return role.bearer[account];
    }
}

File 17 of 20 : FixidityLib.sol
pragma solidity ^0.5.0;


/**
 * @title FixidityLib
 * @author Gadi Guy, Alberto Cuesta Canada
 * @notice This library provides fixed point arithmetic with protection against
 * overflow. 
 * All operations are done with int256 and the operands must have been created 
 * with any of the newFrom* functions, which shift the comma digits() to the 
 * right and check for limits.
 * When using this library be sure of using maxNewFixed() as the upper limit for
 * creation of fixed point numbers. Use maxFixedMul(), maxFixedDiv() and
 * maxFixedAdd() if you want to be certain that those operations don't 
 * overflow.
 */
library FixidityLib {

    /**
     * @notice Number of positions that the comma is shifted to the right.
     */
    function digits() public pure returns(uint8) {
        return 24;
    }
    
    /**
     * @notice This is 1 in the fixed point units used in this library.
     * @dev Test fixed1() equals 10^digits()
     * Hardcoded to 24 digits.
     */
    function fixed1() public pure returns(int256) {
        return 1000000000000000000000000;
    }

    /**
     * @notice The amount of decimals lost on each multiplication operand.
     * @dev Test mulPrecision() equals sqrt(fixed1)
     * Hardcoded to 24 digits.
     */
    function mulPrecision() public pure returns(int256) {
        return 1000000000000;
    }

    /**
     * @notice Maximum value that can be represented in an int256
     * @dev Test maxInt256() equals 2^255 -1
     */
    function maxInt256() public pure returns(int256) {
        return 57896044618658097711785492504343953926634992332820282019728792003956564819967;
    }

    /**
     * @notice Minimum value that can be represented in an int256
     * @dev Test minInt256 equals (2^255) * (-1)
     */
    function minInt256() public pure returns(int256) {
        return -57896044618658097711785492504343953926634992332820282019728792003956564819968;
    }

    /**
     * @notice Maximum value that can be converted to fixed point. Optimize for
     * @dev deployment. 
     * Test maxNewFixed() equals maxInt256() / fixed1()
     * Hardcoded to 24 digits.
     */
    function maxNewFixed() public pure returns(int256) {
        return 57896044618658097711785492504343953926634992332820282;
    }

    /**
     * @notice Minimum value that can be converted to fixed point. Optimize for
     * deployment. 
     * @dev Test minNewFixed() equals -(maxInt256()) / fixed1()
     * Hardcoded to 24 digits.
     */
    function minNewFixed() public pure returns(int256) {
        return -57896044618658097711785492504343953926634992332820282;
    }

    /**
     * @notice Maximum value that can be safely used as an addition operator.
     * @dev Test maxFixedAdd() equals maxInt256()-1 / 2
     * Test add(maxFixedAdd(),maxFixedAdd()) equals maxFixedAdd() + maxFixedAdd()
     * Test add(maxFixedAdd()+1,maxFixedAdd()) throws 
     * Test add(-maxFixedAdd(),-maxFixedAdd()) equals -maxFixedAdd() - maxFixedAdd()
     * Test add(-maxFixedAdd(),-maxFixedAdd()-1) throws 
     */
    function maxFixedAdd() public pure returns(int256) {
        return 28948022309329048855892746252171976963317496166410141009864396001978282409983;
    }

    /**
     * @notice Maximum negative value that can be safely in a subtraction.
     * @dev Test maxFixedSub() equals minInt256() / 2
     */
    function maxFixedSub() public pure returns(int256) {
        return -28948022309329048855892746252171976963317496166410141009864396001978282409984;
    }

    /**
     * @notice Maximum value that can be safely used as a multiplication operator.
     * @dev Calculated as sqrt(maxInt256()*fixed1()). 
     * Be careful with your sqrt() implementation. I couldn't find a calculator
     * that would give the exact square root of maxInt256*fixed1 so this number
     * is below the real number by no more than 3*10**28. It is safe to use as
     * a limit for your multiplications, although powers of two of numbers over
     * this value might still work.
     * Test multiply(maxFixedMul(),maxFixedMul()) equals maxFixedMul() * maxFixedMul()
     * Test multiply(maxFixedMul(),maxFixedMul()+1) throws 
     * Test multiply(-maxFixedMul(),maxFixedMul()) equals -maxFixedMul() * maxFixedMul()
     * Test multiply(-maxFixedMul(),maxFixedMul()+1) throws 
     * Hardcoded to 24 digits.
     */
    function maxFixedMul() public pure returns(int256) {
        return 240615969168004498257251713877715648331380787511296;
    }

    /**
     * @notice Maximum value that can be safely used as a dividend.
     * @dev divide(maxFixedDiv,newFixedFraction(1,fixed1())) = maxInt256().
     * Test maxFixedDiv() equals maxInt256()/fixed1()
     * Test divide(maxFixedDiv(),multiply(mulPrecision(),mulPrecision())) = maxFixedDiv()*(10^digits())
     * Test divide(maxFixedDiv()+1,multiply(mulPrecision(),mulPrecision())) throws
     * Hardcoded to 24 digits.
     */
    function maxFixedDiv() public pure returns(int256) {
        return 57896044618658097711785492504343953926634992332820282;
    }

    /**
     * @notice Maximum value that can be safely used as a divisor.
     * @dev Test maxFixedDivisor() equals fixed1()*fixed1() - Or 10**(digits()*2)
     * Test divide(10**(digits()*2 + 1),10**(digits()*2)) = returns 10*fixed1()
     * Test divide(10**(digits()*2 + 1),10**(digits()*2 + 1)) = throws
     * Hardcoded to 24 digits.
     */
    function maxFixedDivisor() public pure returns(int256) {
        return 1000000000000000000000000000000000000000000000000;
    }

    /**
     * @notice Converts an int256 to fixed point units, equivalent to multiplying
     * by 10^digits().
     * @dev Test newFixed(0) returns 0
     * Test newFixed(1) returns fixed1()
     * Test newFixed(maxNewFixed()) returns maxNewFixed() * fixed1()
     * Test newFixed(maxNewFixed()+1) fails
     */
    function newFixed(int256 x)
        public
        pure
        returns (int256)
    {
        require(x <= maxNewFixed());
        require(x >= minNewFixed());
        return x * fixed1();
    }

    /**
     * @notice Converts an int256 in the fixed point representation of this 
     * library to a non decimal. All decimal digits will be truncated.
     */
    function fromFixed(int256 x)
        public
        pure
        returns (int256)
    {
        return x / fixed1();
    }

    /**
     * @notice Converts an int256 which is already in some fixed point 
     * representation to a different fixed precision representation.
     * Both the origin and destination precisions must be 38 or less digits.
     * Origin values with a precision higher than the destination precision
     * will be truncated accordingly.
     * @dev 
     * Test convertFixed(1,0,0) returns 1;
     * Test convertFixed(1,1,1) returns 1;
     * Test convertFixed(1,1,0) returns 0;
     * Test convertFixed(1,0,1) returns 10;
     * Test convertFixed(10,1,0) returns 1;
     * Test convertFixed(10,0,1) returns 100;
     * Test convertFixed(100,1,0) returns 10;
     * Test convertFixed(100,0,1) returns 1000;
     * Test convertFixed(1000,2,0) returns 10;
     * Test convertFixed(1000,0,2) returns 100000;
     * Test convertFixed(1000,2,1) returns 100;
     * Test convertFixed(1000,1,2) returns 10000;
     * Test convertFixed(maxInt256,1,0) returns maxInt256/10;
     * Test convertFixed(maxInt256,0,1) throws
     * Test convertFixed(maxInt256,38,0) returns maxInt256/(10**38);
     * Test convertFixed(1,0,38) returns 10**38;
     * Test convertFixed(maxInt256,39,0) throws
     * Test convertFixed(1,0,39) throws
     */
    function convertFixed(int256 x, uint8 _originDigits, uint8 _destinationDigits)
        public
        pure
        returns (int256)
    {
        require(_originDigits <= 38 && _destinationDigits <= 38);
        
        uint8 decimalDifference;
        if ( _originDigits > _destinationDigits ){
            decimalDifference = _originDigits - _destinationDigits;
            return x/(uint128(10)**uint128(decimalDifference));
        }
        else if ( _originDigits < _destinationDigits ){
            decimalDifference = _destinationDigits - _originDigits;
            // Cast uint8 -> uint128 is safe
            // Exponentiation is safe:
            //     _originDigits and _destinationDigits limited to 38 or less
            //     decimalDifference = abs(_destinationDigits - _originDigits)
            //     decimalDifference < 38
            //     10**38 < 2**128-1
            require(x <= maxInt256()/uint128(10)**uint128(decimalDifference));
            require(x >= minInt256()/uint128(10)**uint128(decimalDifference));
            return x*(uint128(10)**uint128(decimalDifference));
        }
        // _originDigits == digits()) 
        return x;
    }

    /**
     * @notice Converts an int256 which is already in some fixed point 
     * representation to that of this library. The _originDigits parameter is the
     * precision of x. Values with a precision higher than FixidityLib.digits()
     * will be truncated accordingly.
     */
    function newFixed(int256 x, uint8 _originDigits)
        public
        pure
        returns (int256)
    {
        return convertFixed(x, _originDigits, digits());
    }

    /**
     * @notice Converts an int256 in the fixed point representation of this 
     * library to a different representation. The _destinationDigits parameter is the
     * precision of the output x. Values with a precision below than 
     * FixidityLib.digits() will be truncated accordingly.
     */
    function fromFixed(int256 x, uint8 _destinationDigits)
        public
        pure
        returns (int256)
    {
        return convertFixed(x, digits(), _destinationDigits);
    }

    /**
     * @notice Converts two int256 representing a fraction to fixed point units,
     * equivalent to multiplying dividend and divisor by 10^digits().
     * @dev 
     * Test newFixedFraction(maxFixedDiv()+1,1) fails
     * Test newFixedFraction(1,maxFixedDiv()+1) fails
     * Test newFixedFraction(1,0) fails     
     * Test newFixedFraction(0,1) returns 0
     * Test newFixedFraction(1,1) returns fixed1()
     * Test newFixedFraction(maxFixedDiv(),1) returns maxFixedDiv()*fixed1()
     * Test newFixedFraction(1,fixed1()) returns 1
     * Test newFixedFraction(1,fixed1()-1) returns 0
     */
    function newFixedFraction(
        int256 numerator, 
        int256 denominator
        )
        public
        pure
        returns (int256)
    {
        require(numerator <= maxNewFixed());
        require(denominator <= maxNewFixed());
        require(denominator != 0);
        int256 convertedNumerator = newFixed(numerator);
        int256 convertedDenominator = newFixed(denominator);
        return divide(convertedNumerator, convertedDenominator);
    }

    /**
     * @notice Returns the integer part of a fixed point number.
     * @dev 
     * Test integer(0) returns 0
     * Test integer(fixed1()) returns fixed1()
     * Test integer(newFixed(maxNewFixed())) returns maxNewFixed()*fixed1()
     * Test integer(-fixed1()) returns -fixed1()
     * Test integer(newFixed(-maxNewFixed())) returns -maxNewFixed()*fixed1()
     */
    function integer(int256 x) public pure returns (int256) {
        return (x / fixed1()) * fixed1(); // Can't overflow
    }

    /**
     * @notice Returns the fractional part of a fixed point number. 
     * In the case of a negative number the fractional is also negative.
     * @dev 
     * Test fractional(0) returns 0
     * Test fractional(fixed1()) returns 0
     * Test fractional(fixed1()-1) returns 10^24-1
     * Test fractional(-fixed1()) returns 0
     * Test fractional(-fixed1()+1) returns -10^24-1
     */
    function fractional(int256 x) public pure returns (int256) {
        return x - (x / fixed1()) * fixed1(); // Can't overflow
    }

    /**
     * @notice Converts to positive if negative.
     * Due to int256 having one more negative number than positive numbers 
     * abs(minInt256) reverts.
     * @dev 
     * Test abs(0) returns 0
     * Test abs(fixed1()) returns -fixed1()
     * Test abs(-fixed1()) returns fixed1()
     * Test abs(newFixed(maxNewFixed())) returns maxNewFixed()*fixed1()
     * Test abs(newFixed(minNewFixed())) returns -minNewFixed()*fixed1()
     */
    function abs(int256 x) public pure returns (int256) {
        if (x >= 0) {
            return x;
        } else {
            int256 result = -x;
            assert (result > 0);
            return result;
        }
    }

    /**
     * @notice x+y. If any operator is higher than maxFixedAdd() it 
     * might overflow.
     * In solidity maxInt256 + 1 = minInt256 and viceversa.
     * @dev 
     * Test add(maxFixedAdd(),maxFixedAdd()) returns maxInt256()-1
     * Test add(maxFixedAdd()+1,maxFixedAdd()+1) fails
     * Test add(-maxFixedSub(),-maxFixedSub()) returns minInt256()
     * Test add(-maxFixedSub()-1,-maxFixedSub()-1) fails
     * Test add(maxInt256(),maxInt256()) fails
     * Test add(minInt256(),minInt256()) fails
     */
    function add(int256 x, int256 y) public pure returns (int256) {
        int256 z = x + y;
        if (x > 0 && y > 0) assert(z > x && z > y);
        if (x < 0 && y < 0) assert(z < x && z < y);
        return z;
    }

    /**
     * @notice x-y. You can use add(x,-y) instead. 
     * @dev Tests covered by add(x,y)
     */
    function subtract(int256 x, int256 y) public pure returns (int256) {
        return add(x,-y);
    }

    /**
     * @notice x*y. If any of the operators is higher than maxFixedMul() it 
     * might overflow.
     * @dev 
     * Test multiply(0,0) returns 0
     * Test multiply(maxFixedMul(),0) returns 0
     * Test multiply(0,maxFixedMul()) returns 0
     * Test multiply(maxFixedMul(),fixed1()) returns maxFixedMul()
     * Test multiply(fixed1(),maxFixedMul()) returns maxFixedMul()
     * Test all combinations of (2,-2), (2, 2.5), (2, -2.5) and (0.5, -0.5)
     * Test multiply(fixed1()/mulPrecision(),fixed1()*mulPrecision())
     * Test multiply(maxFixedMul()-1,maxFixedMul()) equals multiply(maxFixedMul(),maxFixedMul()-1)
     * Test multiply(maxFixedMul(),maxFixedMul()) returns maxInt256() // Probably not to the last digits
     * Test multiply(maxFixedMul()+1,maxFixedMul()) fails
     * Test multiply(maxFixedMul(),maxFixedMul()+1) fails
     */
    function multiply(int256 x, int256 y) public pure returns (int256) {
        if (x == 0 || y == 0) return 0;
        if (y == fixed1()) return x;
        if (x == fixed1()) return y;

        // Separate into integer and fractional parts
        // x = x1 + x2, y = y1 + y2
        int256 x1 = integer(x) / fixed1();
        int256 x2 = fractional(x);
        int256 y1 = integer(y) / fixed1();
        int256 y2 = fractional(y);
        
        // (x1 + x2) * (y1 + y2) = (x1 * y1) + (x1 * y2) + (x2 * y1) + (x2 * y2)
        int256 x1y1 = x1 * y1;
        if (x1 != 0) assert(x1y1 / x1 == y1); // Overflow x1y1
        
        // x1y1 needs to be multiplied back by fixed1
        // solium-disable-next-line mixedcase
        int256 fixed_x1y1 = x1y1 * fixed1();
        if (x1y1 != 0) assert(fixed_x1y1 / x1y1 == fixed1()); // Overflow x1y1 * fixed1
        x1y1 = fixed_x1y1;

        int256 x2y1 = x2 * y1;
        if (x2 != 0) assert(x2y1 / x2 == y1); // Overflow x2y1

        int256 x1y2 = x1 * y2;
        if (x1 != 0) assert(x1y2 / x1 == y2); // Overflow x1y2

        x2 = x2 / mulPrecision();
        y2 = y2 / mulPrecision();
        int256 x2y2 = x2 * y2;
        if (x2 != 0) assert(x2y2 / x2 == y2); // Overflow x2y2

        // result = fixed1() * x1 * y1 + x1 * y2 + x2 * y1 + x2 * y2 / fixed1();
        int256 result = x1y1;
        result = add(result, x2y1); // Add checks for overflow
        result = add(result, x1y2); // Add checks for overflow
        result = add(result, x2y2); // Add checks for overflow
        return result;
    }
    
    /**
     * @notice 1/x
     * @dev 
     * Test reciprocal(0) fails
     * Test reciprocal(fixed1()) returns fixed1()
     * Test reciprocal(fixed1()*fixed1()) returns 1 // Testing how the fractional is truncated
     * Test reciprocal(2*fixed1()*fixed1()) returns 0 // Testing how the fractional is truncated
     */
    function reciprocal(int256 x) public pure returns (int256) {
        require(x != 0);
        return (fixed1()*fixed1()) / x; // Can't overflow
    }

    /**
     * @notice x/y. If the dividend is higher than maxFixedDiv() it 
     * might overflow. You can use multiply(x,reciprocal(y)) instead.
     * There is a loss of precision on division for the lower mulPrecision() decimals.
     * @dev 
     * Test divide(fixed1(),0) fails
     * Test divide(maxFixedDiv(),1) = maxFixedDiv()*(10^digits())
     * Test divide(maxFixedDiv()+1,1) throws
     * Test divide(maxFixedDiv(),maxFixedDiv()) returns fixed1()
     */
    function divide(int256 x, int256 y) public pure returns (int256) {
        if (y == fixed1()) return x;
        require(y != 0);
        require(y <= maxFixedDivisor());
        return multiply(x, reciprocal(y));
    }
}

File 18 of 20 : Initializable.sol
pragma solidity >=0.4.24 <0.6.0;


/**
 * @title Initializable
 *
 * @dev Helper contract to support initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 */
contract Initializable {

  /**
   * @dev Indicates that the contract has been initialized.
   */
  bool private initialized;

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

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");

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

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }

  /// @dev Returns true if and only if the function is running in the constructor
  function isConstructor() private view returns (bool) {
    // extcodesize checks the size of the code stored in an address, and
    // address returns the current address. Since the code is still not
    // deployed when running a constructor, any checks on its code size will
    // yield zero, making it an effective way to detect if a contract is
    // under construction or not.
    uint256 cs;
    assembly { cs := extcodesize(address) }
    return cs == 0;
  }

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

File 19 of 20 : ERC20Mintable.sol
pragma solidity ^0.5.2;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "./ERC20.sol";
import "../../access/roles/MinterRole.sol";

/**
 * @title ERC20Mintable
 * @dev ERC20 minting logic
 */
contract ERC20Mintable is Initializable, ERC20, MinterRole {
    function initialize(address sender) public initializer {
        MinterRole.initialize(sender);
    }

    /**
     * @dev Function to mint tokens
     * @param to The address that will receive the minted tokens.
     * @param value The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(address to, uint256 value) public onlyMinter returns (bool) {
        _mint(to, value);
        return true;
    }

    uint256[50] private ______gap;
}

File 20 of 20 : ERC20.sol
pragma solidity ^0.5.2;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://eips.ethereum.org/EIPS/eip-20
 * Originally based on code by FirstBlood:
 * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 *
 * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
 * all accounts just by listening to said events. Note that this isn't required by the specification, and other
 * compliant implementations may not do it.
 */
contract ERC20 is Initializable, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

    /**
     * @dev Total number of tokens in existence
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev Gets the balance of the specified address.
     * @param owner The address to query the balance of.
     * @return A uint256 representing the amount owned by the passed address.
     */
    function balanceOf(address owner) public view returns (uint256) {
        return _balances[owner];
    }

    /**
     * @dev Function to check the amount of tokens that an owner allowed to a spender.
     * @param owner address The address which owns the funds.
     * @param spender address The address which will spend the funds.
     * @return A uint256 specifying the amount of tokens still available for the spender.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowed[owner][spender];
    }

    /**
     * @dev Transfer token to a specified address
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function transfer(address to, uint256 value) public returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * 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
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     */
    function approve(address spender, uint256 value) public returns (bool) {
        _approve(msg.sender, spender, value);
        return true;
    }

    /**
     * @dev Transfer tokens from one address to another.
     * Note that while this function emits an Approval event, this is not required as per the specification,
     * and other compliant implementations may not emit the event.
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param value uint256 the amount of tokens to be transferred
     */
    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        _transfer(from, to, value);
        _approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
        return true;
    }

    /**
     * @dev Increase the amount of tokens that an owner allowed to a spender.
     * approve should be called when _allowed[msg.sender][spender] == 0. To increment
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * Emits an Approval event.
     * @param spender The address which will spend the funds.
     * @param addedValue The amount of tokens to increase the allowance by.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Decrease the amount of tokens that an owner allowed to a spender.
     * approve should be called when _allowed[msg.sender][spender] == 0. To decrement
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * Emits an Approval event.
     * @param spender The address which will spend the funds.
     * @param subtractedValue The amount of tokens to decrease the allowance by.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
        return true;
    }

    /**
     * @dev Transfer token for a specified addresses
     * @param from The address to transfer from.
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function _transfer(address from, address to, uint256 value) internal {
        require(to != address(0));

        _balances[from] = _balances[from].sub(value);
        _balances[to] = _balances[to].add(value);
        emit Transfer(from, to, value);
    }

    /**
     * @dev Internal function that mints an amount of the token and assigns it to
     * an account. This encapsulates the modification of balances such that the
     * proper events are emitted.
     * @param account The account that will receive the created tokens.
     * @param value The amount that will be created.
     */
    function _mint(address account, uint256 value) internal {
        require(account != address(0));

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

    /**
     * @dev Internal function that burns an amount of the token of a given
     * account.
     * @param account The account whose tokens will be burnt.
     * @param value The amount that will be burnt.
     */
    function _burn(address account, uint256 value) internal {
        require(account != address(0));

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

    /**
     * @dev Approve an address to spend another addresses' tokens.
     * @param owner The address that owns the tokens.
     * @param spender The address that will spend the tokens.
     * @param value The number of tokens that can be spent.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        require(spender != address(0));
        require(owner != address(0));

        _allowed[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    /**
     * @dev Internal function that burns an amount of the token of a given
     * account, deducting from the sender's allowance for said account. Uses the
     * internal burn function.
     * Emits an Approval event (reflecting the reduced allowance).
     * @param account The account whose tokens will be burnt.
     * @param value The amount that will be burnt.
     */
    function _burnFrom(address account, uint256 value) internal {
        _burn(account, value);
        _approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
    }

    uint256[50] private ______gap;
}

File 21 of 20 : MinterRole.sol
pragma solidity ^0.5.2;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "../Roles.sol";


contract MinterRole is Initializable {
    using Roles for Roles.Role;

    event MinterAdded(address indexed account);
    event MinterRemoved(address indexed account);

    Roles.Role private _minters;

    function initialize(address sender) public initializer {
        if (!isMinter(sender)) {
            _addMinter(sender);
        }
    }

    modifier onlyMinter() {
        require(isMinter(msg.sender));
        _;
    }

    function isMinter(address account) public view returns (bool) {
        return _minters.has(account);
    }

    function addMinter(address account) public onlyMinter {
        _addMinter(account);
    }

    function renounceMinter() public {
        _removeMinter(msg.sender);
    }

    function _addMinter(address account) internal {
        _minters.add(account);
        emit MinterAdded(account);
    }

    function _removeMinter(address account) internal {
        _minters.remove(account);
        emit MinterRemoved(account);
    }

    uint256[50] private ______gap;
}

Settings
{
  "optimizer": {},
  "evmVersion": "constantinople",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/Pool.sol": {
      "DrawManager": "0xD215CF8D8bC151414A9c5c145fE219E746E5cE80",
      "FixidityLib": "0x5Ce0678E90719ff5382a57fA693394Aee468f11B"
    }
  }
}

Contract Security Audit

Contract ABI

[{"constant":true,"inputs":[],"name":"accountedBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextFeeFraction","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_admin","type":"address"}],"name":"removeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"depositPool","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_admin","type":"address"}],"name":"isAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentOpenDrawId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_feeBeneficiary","type":"address"}],"name":"setNextFeeBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"depositSponsorship","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"openSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_cToken","type":"address"},{"name":"_feeFraction","type":"uint256"},{"name":"_feeBeneficiary","type":"address"}],"name":"init","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"openBalanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_admin","type":"address"}],"name":"addAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_blocks","type":"uint256"}],"name":"estimatedInterestRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"committedBalanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentCommittedDrawId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"nextSecretHash","type":"bytes32"},{"name":"lastSecret","type":"bytes32"},{"name":"_salt","type":"bytes32"}],"name":"rewardAndOpenNextDraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_feeFraction","type":"uint256"}],"name":"setNextFeeFraction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nextFeeBeneficiary","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_entropy","type":"bytes32"}],"name":"calculateWinner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"supplyRatePerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"balance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_drawId","type":"uint256"}],"name":"getDraw","outputs":[{"name":"feeFraction","type":"uint256"},{"name":"feeBeneficiary","type":"address"},{"name":"openedBlock","type":"uint256"},{"name":"secretHash","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"nextSecretHash","type":"bytes32"}],"name":"openNextDraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"committedSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"SponsorshipDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"}],"name":"AdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"}],"name":"AdminRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"drawId","type":"uint256"},{"indexed":true,"name":"feeBeneficiary","type":"address"},{"indexed":false,"name":"secretHash","type":"bytes32"},{"indexed":false,"name":"feeFraction","type":"uint256"}],"name":"Opened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"drawId","type":"uint256"}],"name":"Committed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"drawId","type":"uint256"},{"indexed":true,"name":"winner","type":"address"},{"indexed":false,"name":"entropy","type":"bytes32"},{"indexed":false,"name":"winnings","type":"uint256"},{"indexed":false,"name":"fee","type":"uint256"}],"name":"Rewarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeFraction","type":"uint256"}],"name":"NextFeeFractionChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"feeBeneficiary","type":"address"}],"name":"NextFeeBeneficiaryChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"}],"name":"Unpaused","type":"event"}]

60806040523480156100115760006000fd5b50610017565b613c3b806100266000396000f3fe60806040523480156100115760006000fd5b50600436106101ea5760003560e01c80637048027511610110578063944b1479116100a3578063b69ef8a811610072578063b69ef8a8146107da578063be6307c8146107f8578063c1ebb4ac14610884578063e8f099f4146108b7576101ea565b8063944b1479146106d05780639ad8bd78146106ff578063ac2defea14610749578063ae9d70b0146107bc576101ea565b80637f77fc4d116100df5780637f77fc4d1461064f5780638129fc1c1461066d578063815d85d2146106775780638456cb59146106c6576101ea565b8063704802751461051557806370a082311461055a57806377bbb757146105b357806379f08771146105f6576101ea565b80633d3d6a1a116101885780634b180da9116101575780634b180da9146103c157806350193485146104505780635c975abb146104a957806369e527da146104cb576101ea565b80633d3d6a1a146103255780633f4ba83a1461036a578063426d58e51461037457806347800068146103a3576101ea565b806323440944116101c4578063234409441461027157806324d7806c146102a0578063304c9670146102fd5780633ccfd60b1461031b576101ea565b80630937eb54146101f057806314f74b8c1461020e5780631785f53c1461022c576101ea565b60006000fd5b6101f86108d5565b6040518082815260200191505060405180910390f35b6102166108de565b6040518082815260200191505060405180910390f35b61026f600480360360208110156102435760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108e7565b005b61029e600480360360208110156102885760006000fd5b8101908080359060200190929190505050610a62565b005b6102e3600480360360208110156102b75760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cb6565b604051808215151515815260200191505060405180910390f35b610305610cdb565b6040518082815260200191505060405180910390f35b610323610cf3565b005b6103686004803603602081101561033c5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ed0565b005b610372610f72565b005b6103a16004803603602081101561038b5760006000fd5b81019080803590602001909291905050506110ea565b005b6103ab611209565b6040518082815260200191505060405180910390f35b61044e600480360360808110156103d85760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112a8565b005b610493600480360360208110156104675760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611577565b6040518082815260200191505060405180910390f35b6104b161164c565b604051808215151515815260200191505060405180910390f35b6104d361165f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105586004803603602081101561052c5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611685565b005b61059d600480360360208110156105715760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611727565b6040518082815260200191505060405180910390f35b6105e0600480360360208110156105ca5760006000fd5b810190808035906020019092919050505061177b565b6040518082815260200191505060405180910390f35b6106396004803603602081101561060d5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117a9565b6040518082815260200191505060405180910390f35b61065761187e565b6040518082815260200191505060405180910390f35b6106756118bb565b005b6106c46004803603606081101561068e5760006000fd5b810190808035600019169060200190929190803560001916906020019092919080356000191690602001909291905050506119d5565b005b6106ce611ba9565b005b6106fd600480360360208110156106e75760006000fd5b8101908080359060200190929190505050611d22565b005b610707611dc4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61077a600480360360208110156107605760006000fd5b810190808035600019169060200190929190505050611dea565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6107c4611e9b565b6040518082815260200191505060405180910390f35b6107e2611f4d565b6040518082815260200191505060405180910390f35b6108256004803603602081101561080f5760006000fd5b8101908080359060200190929190505050612038565b604051808581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001826000191660001916815260200194505050505060405180910390f35b6108b56004803603602081101561089b5760006000fd5b8101908080356000191690602001909291905050506120b3565b005b6108bf61228c565b6040518082815260200191505060405180910390f35b60696000505481565b60686000505481565b6108fe3360726000506122a490919063ffffffff16565b1515610975576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b61098c8160726000506122a490919063ffffffff16565b1515610a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f61646d696e20646f6573206e6f7420657869737400000000000000000000000081526020015060200191505060405180910390fd5b610a1a81607260005061234290919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f60405160405180910390a25b5b50565b6000610a72610cdb63ffffffff16565b14151515610aeb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f7468657265206973206e6f206f70656e2064726177000000000000000000000081526020015060200191505060405180910390fd5b60016033600082828250540192505081909090555060006033600050549050607360009054906101000a900460ff16151515610b92576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e3451b76909133856040518463ffffffff1660e01b8152600401808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060006040518083038186803b158015610c245760006000fd5b505af4158015610c39573d600060003e3d6000fd5b50505050610c4c826123fd63ffffffff16565b3373ffffffffffffffffffffffffffffffffffffffff167f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4836040518082815260200191505060405180910390a25b5b60336000505481141515610cb05760006000fd5b505b5b50565b6000610ccf8260726000506122a490919063ffffffff16565b9050610cd6565b919050565b6000606c600050600401600050549050610cf0565b90565b600160336000828282505401925050819090905550600060336000505490506000606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050600081111515610db7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180613b706022913960400191505060405180910390fd5b6000606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80634c9b9e9c9091336040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b158015610e8e5760006000fd5b505af4158015610ea3573d600060003e3d6000fd5b50505050610eb68161293763ffffffff16565b505b60336000505481141515610ecc5760006000fd5b505b565b610ee73360726000506122a490919063ffffffff16565b1515610f5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b610f6d81612c7d63ffffffff16565b5b5b50565b607360009054906101000a900460ff161515610ff9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f636f6e7472616374206973206e6f74207061757365640000000000000000000081526020015060200191505060405180910390fd5b6110103360726000506122a490919063ffffffff16565b1515611087576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b6000607360006101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff167f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa60405160405180910390a25b5b5b565b60016033600082828250540192505081909090555060006033600050549050607360009054906101000a900460ff16151515611191576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b6111a0826123fd63ffffffff16565b3373ffffffffffffffffffffffffffffffffffffffff167f6dd4ea9218ce2f17ec77769fa65225b906e99dd3f597b7e087df3bdd8f7899dd836040518082815260200191505060405180910390a25b5b603360005054811415156112045760006000fd5b505b50565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063ed21d6ea90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156112615760006000fd5b505af4158015611276573d600060003e3d6000fd5b505050506040513d602081101561128d5760006000fd5b810190808051906020019092919050505090506112a5565b90565b600060019054906101000a900460ff16806112cd57506112cc612dad63ffffffff16565b5b806112e55750600060009054906101000a900460ff16155b151561133c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613bb5602e913960400191505060405180910390fd5b6000600060019054906101000a900460ff16159050801561138e576001600060016101000a81548160ff0219169083151502179055506001600060006101000a81548160ff0219169083151502179055505b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614151515611436576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6f776e65722063616e6e6f7420626520746865206e756c6c206164647265737381526020015060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141515156114de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f6d6f6e6579206d61726b65742061646472657373206973207a65726f0000000081526020015060200191505060405180910390fd5b83606660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061152e85612dc563ffffffff16565b61153d83612e2363ffffffff16565b61154c82612c7d63ffffffff16565b5b801561156f576000600060016101000a81548160ff0219169083151502179055505b505b50505050565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e27683ea9091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156116035760006000fd5b505af4158015611618573d600060003e3d6000fd5b505050506040513d602081101561162f5760006000fd5b81019080805190602001909291905050509050611647565b919050565b607360009054906101000a900460ff1681565b606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61169c3360726000506122a490919063ffffffff16565b1515611713576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b61172281612dc563ffffffff16565b5b5b50565b6000606a60005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050611776565b919050565b600061179d8261178f611e9b63ffffffff16565b612f6990919063ffffffff16565b90506117a4565b919050565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80638fa1e1989091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156118355760006000fd5b505af415801561184a573d600060003e3d6000fd5b505050506040513d60208110156118615760006000fd5b81019080805190602001909291905050509050611879565b919050565b60006001606c6000506004016000505411156118ae576001606c600050600401600050540390506118b8566118b7565b600090506118b8565b5b90565b600060019054906101000a900460ff16806118e057506118df612dad63ffffffff16565b5b806118f85750600060009054906101000a900460ff16155b151561194f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613bb5602e913960400191505060405180910390fd5b6000600060019054906101000a900460ff1615905080156119a1576001600060016101000a81548160ff0219169083151502179055506001600060006101000a81548160ff0219169083151502179055505b600160336000508190909055505b80156119d1576000600060016101000a81548160ff0219169083151502179055505b505b565b6119ec3360726000506122a490919063ffffffff16565b1515611a63576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b607360009054906101000a900460ff16151515611aeb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b6000611afb61187e63ffffffff16565b14151515611b74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f61206472617720686173206e6f74206265656e20636f6d6d697474656400000081526020015060200191505060405180910390fd5b611b848282612fae63ffffffff16565b611b926134da63ffffffff16565b611ba18361351d63ffffffff16565b5b5b5b505050565b607360009054906101000a900460ff16151515611c31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b611c483360726000506122a490919063ffffffff16565b1515611cbf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b6001607360006101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff167f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25860405160405180910390a25b5b5b565b611d393360726000506122a490919063ffffffff16565b1515611db0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b611dbf81612e2363ffffffff16565b5b5b50565b606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80633484b6bb9091846040518363ffffffff1660e01b81526004018083815260200182600019166000191681526020019250505060206040518083038186803b158015611e525760006000fd5b505af4158015611e67573d600060003e3d6000fd5b505050506040513d6020811015611e7e5760006000fd5b81019080805190602001909291905050509050611e96565b919050565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ae9d70b06040518163ffffffff1660e01b815260040160206040518083038186803b158015611f065760006000fd5b505afa158015611f1b573d600060003e3d6000fd5b505050506040513d6020811015611f325760006000fd5b81019080805190602001909291905050509050611f4a565b90565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633af9e669306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015611ff15760006000fd5b505af1158015612006573d600060003e3d6000fd5b505050506040513d602081101561201d5760006000fd5b81019080805190602001909291905050509050612035565b90565b60006000600060006000606b600050600087815260200190815260200160002060005090508060000160005054945084508060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1693508350806002016000505492508250806003016000505491508150505b9193509193565b6120ca3360726000506122a490919063ffffffff16565b1515612141576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b607360009054906101000a900460ff161515156121c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b60006121d961187e63ffffffff16565b141515612251576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f7468657265206973206120636f6d6d697474656420647261770000000000000081526020015060200191505060405180910390fd5b6000612261610cdb63ffffffff16565b141515612277576122766134da63ffffffff16565b5b6122868161351d63ffffffff16565b5b5b5b50565b6000606c6000506005016000505490506122a1565b90565b6000600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141515156122e35760006000fd5b8260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905061233c565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415151561237f5760006000fd5b61238f82826122a463ffffffff16565b151561239b5760006000fd5b60008260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b5050565b600081111515612478576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6465706f736974206973206e6f742067726561746572207468616e207a65726f81526020015060200191505060405180910390fd5b61248661374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd3330846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b1580156125415760006000fd5b505af1158015612556573d600060003e3d6000fd5b505050506040513d602081101561256d5760006000fd5b810190808051906020019092919050505015156125f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f746f6b656e207472616e73666572206661696c6564000000000000000000000081526020015060200191505060405180910390fd5b61264d81606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000508190909055506126b0816069600050546137fc90919063ffffffff16565b60696000508190909055506126c961374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b3606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156127725760006000fd5b505af1158015612787573d600060003e3d6000fd5b505050506040513d602081101561279e5760006000fd5b81019080805190602001909291905050501515612806576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180613be36024913960400191505060405180910390fd5b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d68836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561287e5760006000fd5b505af1158015612893573d600060003e3d6000fd5b505050506040513d60208110156128aa5760006000fd5b8101908080519060200190929190505050141515612933576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f636f756c64206e6f7420737570706c79206d6f6e6579206d61726b657400000081526020015060200191505060405180910390fd5b5b50565b600081111515612992576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180613b926023913960400191505060405180910390fd5b6129aa8160696000505461382590919063ffffffff16565b60696000508190909055506000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663852a12e3836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015612a2d5760006000fd5b505af1158015612a42573d600060003e3d6000fd5b505050506040513d6020811015612a595760006000fd5b8101908080519060200190929190505050141515612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f636f756c64206e6f742072656465656d2066726f6d20636f6d706f756e64000081526020015060200191505060405180910390fd5b612af061374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015612b775760006000fd5b505af1158015612b8c573d600060003e3d6000fd5b505050506040513d6020811015612ba35760006000fd5b81019080805190602001909291905050501515612c2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f636f756c64206e6f74207472616e736665722077696e6e696e6773000000000081526020015060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5826040518082815260200191505060405180910390a25b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151515612d25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f62656e65666963696172792073686f756c64206e6f742062652030783000000081526020015060200191505060405180910390fd5b80606760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f4adde74fa6a2bac1c22b89b0488eb67527c033fc6110f443d1424a91a0d41d4560405160405180910390a25b50565b60006000303b905060008114915050612dc256505b90565b612ddc81607260005061384e90919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33960405160405180910390a25b50565b60008110151515612e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f666565206d757374206265207a65726f206f722067726561746572000000000081526020015060200191505060405180910390fd5b670de0b6b3a76400008111151515612f22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f666565206672616374696f6e206d7573742062652031206f72206c657373000081526020015060200191505060405180910390fd5b8060686000508190909055507f19cbde830537adec39ff348fcf33c89911750be4bc7433a01b3836d71ddb7881816040518082815260200191505060405180910390a15b50565b60006000831415612f7d5760009050612fa8565b60008284029050828482811515612f9057fe5b04141515612f9e5760006000fd5b80915050612fa856505b92915050565b6000612fbe61187e63ffffffff16565b90506000606b60005060008381526020019081526020016000206000509050838360405160200180836000191660001916815260200182600019166000191681526020019250505060405160208183030381529060405280519060200120600019168160030160005054600019161415156130a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f73656372657420646f6573206e6f74206d61746368000000000000000000000081526020015060200191505060405180910390fd5b60006130b4611f4d63ffffffff16565b905060006130d06069600050548361382590919063ffffffff16565b9050600083600201600050548260405160200180838152602001828152602001925050506040516020818303038152906040528051906020012087189050600061311f82611dea63ffffffff16565b9050600061313a86600001600050548561390a63ffffffff16565b90506131b881606a60005060008960010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060008860010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550600061323c828661382590919063ffffffff16565b9050600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561327c575060008114155b156133e0576132d981606a60005060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e3451b76909185846040518463ffffffff1660e01b8152600401808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060006040518083038186803b1580156133b65760006000fd5b505af41580156133cb573d600060003e3d6000fd5b50505050856069600050819090905550613404565b6133f8826069600050546137fc90919063ffffffff16565b60696000508190909055505b606b6000506000898152602001908152602001600020600060008201600050600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160005060009055600382016000506000905550508273ffffffffffffffffffffffffffffffffffffffff16887f39d270b67baa0bff7a394d3427e52a85d706cae15e649754ec7b54f3c9deb3f0868486604051808460001916600019168152602001838152602001828152602001935050505060405180910390a350505050505050505b5050565b60006134ea610cdb63ffffffff16565b9050807f023ad9f3cfd45bbf91919354cab651602c11b3d4267df2f095331f1e31c0c42960405160405180910390a2505b565b606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80636290728890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156135735760006000fd5b505af4158015613588573d600060003e3d6000fd5b505050506040513d602081101561359f5760006000fd5b81019080805190602001909291905050505060405180608001604052806068600050548152602001606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020014381526020018260001916815260200150606b6000506000606c60005060040160005054815260200190815260200160002060005060008201518160000160005090905560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160005090905560608201518160030160005090600019169055905050606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16606c600050600401600050547f3ba93e35d4f024f23249948504642bc624ab65bc80542daab33f8583f1b8d72f836068600050546040518083600019166000191681526020018281526020019250505060405180910390a35b50565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b815260040160206040518083038186803b1580156137b55760006000fd5b505afa1580156137ca573d600060003e3d6000fd5b505050506040513d60208110156137e15760006000fd5b810190808051906020019092919050505090506137f9565b90565b6000600082840190508381101515156138155760006000fd5b8091505061381f56505b92915050565b60008282111515156138375760006000fd5b600082840390508091505061384856505b92915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415151561388b5760006000fd5b61389b82826122a463ffffffff16565b1515156138a85760006000fd5b60018260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b5050565b60006000735ce0678e90719ff5382a57fa693394aee468f11b63bd5cbd62846040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561395e5760006000fd5b505af4158015613973573d600060003e3d6000fd5b505050506040513d602081101561398a5760006000fd5b810190808051906020019092919050505090506000735ce0678e90719ff5382a57fa693394aee468f11b633c4308a883735ce0678e90719ff5382a57fa693394aee468f11b63d6c1528b8960126040518363ffffffff1660e01b8152600401808381526020018260ff1660ff1681526020019250505060206040518083038186803b158015613a195760006000fd5b505af4158015613a2e573d600060003e3d6000fd5b505050506040513d6020811015613a455760006000fd5b81019080805190602001909291905050506040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015613a925760006000fd5b505af4158015613aa7573d600060003e3d6000fd5b505050506040513d6020811015613abe5760006000fd5b81019080805190602001909291905050509050735ce0678e90719ff5382a57fa693394aee468f11b630e998993826040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613b215760006000fd5b505af4158015613b36573d600060003e3d6000fd5b505050506040513d6020811015613b4d5760006000fd5b810190808051906020019092919050505092505050613b695650505b9291505056fe62616c616e63652068617320616c7265616479206265656e2077697468647261776e7769746864726177616c206973206e6f742067726561746572207468616e207a65726f436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a6564636f756c64206e6f7420617070726f7665206d6f6e6579206d61726b6574207370656e64a265627a7a723058204f56aea8ae7585b4254f75cd16f60cfe6d95ca3c3081d15b87c30f009647b05964736f6c634300050a0032

Deployed Bytecode

0x60806040523480156100115760006000fd5b50600436106101ea5760003560e01c80637048027511610110578063944b1479116100a3578063b69ef8a811610072578063b69ef8a8146107da578063be6307c8146107f8578063c1ebb4ac14610884578063e8f099f4146108b7576101ea565b8063944b1479146106d05780639ad8bd78146106ff578063ac2defea14610749578063ae9d70b0146107bc576101ea565b80637f77fc4d116100df5780637f77fc4d1461064f5780638129fc1c1461066d578063815d85d2146106775780638456cb59146106c6576101ea565b8063704802751461051557806370a082311461055a57806377bbb757146105b357806379f08771146105f6576101ea565b80633d3d6a1a116101885780634b180da9116101575780634b180da9146103c157806350193485146104505780635c975abb146104a957806369e527da146104cb576101ea565b80633d3d6a1a146103255780633f4ba83a1461036a578063426d58e51461037457806347800068146103a3576101ea565b806323440944116101c4578063234409441461027157806324d7806c146102a0578063304c9670146102fd5780633ccfd60b1461031b576101ea565b80630937eb54146101f057806314f74b8c1461020e5780631785f53c1461022c576101ea565b60006000fd5b6101f86108d5565b6040518082815260200191505060405180910390f35b6102166108de565b6040518082815260200191505060405180910390f35b61026f600480360360208110156102435760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108e7565b005b61029e600480360360208110156102885760006000fd5b8101908080359060200190929190505050610a62565b005b6102e3600480360360208110156102b75760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cb6565b604051808215151515815260200191505060405180910390f35b610305610cdb565b6040518082815260200191505060405180910390f35b610323610cf3565b005b6103686004803603602081101561033c5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ed0565b005b610372610f72565b005b6103a16004803603602081101561038b5760006000fd5b81019080803590602001909291905050506110ea565b005b6103ab611209565b6040518082815260200191505060405180910390f35b61044e600480360360808110156103d85760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112a8565b005b610493600480360360208110156104675760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611577565b6040518082815260200191505060405180910390f35b6104b161164c565b604051808215151515815260200191505060405180910390f35b6104d361165f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105586004803603602081101561052c5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611685565b005b61059d600480360360208110156105715760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611727565b6040518082815260200191505060405180910390f35b6105e0600480360360208110156105ca5760006000fd5b810190808035906020019092919050505061177b565b6040518082815260200191505060405180910390f35b6106396004803603602081101561060d5760006000fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117a9565b6040518082815260200191505060405180910390f35b61065761187e565b6040518082815260200191505060405180910390f35b6106756118bb565b005b6106c46004803603606081101561068e5760006000fd5b810190808035600019169060200190929190803560001916906020019092919080356000191690602001909291905050506119d5565b005b6106ce611ba9565b005b6106fd600480360360208110156106e75760006000fd5b8101908080359060200190929190505050611d22565b005b610707611dc4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61077a600480360360208110156107605760006000fd5b810190808035600019169060200190929190505050611dea565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6107c4611e9b565b6040518082815260200191505060405180910390f35b6107e2611f4d565b6040518082815260200191505060405180910390f35b6108256004803603602081101561080f5760006000fd5b8101908080359060200190929190505050612038565b604051808581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001826000191660001916815260200194505050505060405180910390f35b6108b56004803603602081101561089b5760006000fd5b8101908080356000191690602001909291905050506120b3565b005b6108bf61228c565b6040518082815260200191505060405180910390f35b60696000505481565b60686000505481565b6108fe3360726000506122a490919063ffffffff16565b1515610975576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b61098c8160726000506122a490919063ffffffff16565b1515610a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f61646d696e20646f6573206e6f7420657869737400000000000000000000000081526020015060200191505060405180910390fd5b610a1a81607260005061234290919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f60405160405180910390a25b5b50565b6000610a72610cdb63ffffffff16565b14151515610aeb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f7468657265206973206e6f206f70656e2064726177000000000000000000000081526020015060200191505060405180910390fd5b60016033600082828250540192505081909090555060006033600050549050607360009054906101000a900460ff16151515610b92576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e3451b76909133856040518463ffffffff1660e01b8152600401808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060006040518083038186803b158015610c245760006000fd5b505af4158015610c39573d600060003e3d6000fd5b50505050610c4c826123fd63ffffffff16565b3373ffffffffffffffffffffffffffffffffffffffff167f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4836040518082815260200191505060405180910390a25b5b60336000505481141515610cb05760006000fd5b505b5b50565b6000610ccf8260726000506122a490919063ffffffff16565b9050610cd6565b919050565b6000606c600050600401600050549050610cf0565b90565b600160336000828282505401925050819090905550600060336000505490506000606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050600081111515610db7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180613b706022913960400191505060405180910390fd5b6000606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80634c9b9e9c9091336040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060006040518083038186803b158015610e8e5760006000fd5b505af4158015610ea3573d600060003e3d6000fd5b50505050610eb68161293763ffffffff16565b505b60336000505481141515610ecc5760006000fd5b505b565b610ee73360726000506122a490919063ffffffff16565b1515610f5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b610f6d81612c7d63ffffffff16565b5b5b50565b607360009054906101000a900460ff161515610ff9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f636f6e7472616374206973206e6f74207061757365640000000000000000000081526020015060200191505060405180910390fd5b6110103360726000506122a490919063ffffffff16565b1515611087576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b6000607360006101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff167f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa60405160405180910390a25b5b5b565b60016033600082828250540192505081909090555060006033600050549050607360009054906101000a900460ff16151515611191576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b6111a0826123fd63ffffffff16565b3373ffffffffffffffffffffffffffffffffffffffff167f6dd4ea9218ce2f17ec77769fa65225b906e99dd3f597b7e087df3bdd8f7899dd836040518082815260200191505060405180910390a25b5b603360005054811415156112045760006000fd5b505b50565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063ed21d6ea90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156112615760006000fd5b505af4158015611276573d600060003e3d6000fd5b505050506040513d602081101561128d5760006000fd5b810190808051906020019092919050505090506112a5565b90565b600060019054906101000a900460ff16806112cd57506112cc612dad63ffffffff16565b5b806112e55750600060009054906101000a900460ff16155b151561133c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613bb5602e913960400191505060405180910390fd5b6000600060019054906101000a900460ff16159050801561138e576001600060016101000a81548160ff0219169083151502179055506001600060006101000a81548160ff0219169083151502179055505b600073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614151515611436576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6f776e65722063616e6e6f7420626520746865206e756c6c206164647265737381526020015060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141515156114de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f6d6f6e6579206d61726b65742061646472657373206973207a65726f0000000081526020015060200191505060405180910390fd5b83606660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061152e85612dc563ffffffff16565b61153d83612e2363ffffffff16565b61154c82612c7d63ffffffff16565b5b801561156f576000600060016101000a81548160ff0219169083151502179055505b505b50505050565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e27683ea9091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156116035760006000fd5b505af4158015611618573d600060003e3d6000fd5b505050506040513d602081101561162f5760006000fd5b81019080805190602001909291905050509050611647565b919050565b607360009054906101000a900460ff1681565b606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61169c3360726000506122a490919063ffffffff16565b1515611713576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b61172281612dc563ffffffff16565b5b5b50565b6000606a60005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050611776565b919050565b600061179d8261178f611e9b63ffffffff16565b612f6990919063ffffffff16565b90506117a4565b919050565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80638fa1e1989091846040518363ffffffff1660e01b8152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038186803b1580156118355760006000fd5b505af415801561184a573d600060003e3d6000fd5b505050506040513d60208110156118615760006000fd5b81019080805190602001909291905050509050611879565b919050565b60006001606c6000506004016000505411156118ae576001606c600050600401600050540390506118b8566118b7565b600090506118b8565b5b90565b600060019054906101000a900460ff16806118e057506118df612dad63ffffffff16565b5b806118f85750600060009054906101000a900460ff16155b151561194f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613bb5602e913960400191505060405180910390fd5b6000600060019054906101000a900460ff1615905080156119a1576001600060016101000a81548160ff0219169083151502179055506001600060006101000a81548160ff0219169083151502179055505b600160336000508190909055505b80156119d1576000600060016101000a81548160ff0219169083151502179055505b505b565b6119ec3360726000506122a490919063ffffffff16565b1515611a63576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b607360009054906101000a900460ff16151515611aeb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b6000611afb61187e63ffffffff16565b14151515611b74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f61206472617720686173206e6f74206265656e20636f6d6d697474656400000081526020015060200191505060405180910390fd5b611b848282612fae63ffffffff16565b611b926134da63ffffffff16565b611ba18361351d63ffffffff16565b5b5b5b505050565b607360009054906101000a900460ff16151515611c31576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b611c483360726000506122a490919063ffffffff16565b1515611cbf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b6001607360006101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff167f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25860405160405180910390a25b5b5b565b611d393360726000506122a490919063ffffffff16565b1515611db0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b611dbf81612e2363ffffffff16565b5b5b50565b606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80633484b6bb9091846040518363ffffffff1660e01b81526004018083815260200182600019166000191681526020019250505060206040518083038186803b158015611e525760006000fd5b505af4158015611e67573d600060003e3d6000fd5b505050506040513d6020811015611e7e5760006000fd5b81019080805190602001909291905050509050611e96565b919050565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ae9d70b06040518163ffffffff1660e01b815260040160206040518083038186803b158015611f065760006000fd5b505afa158015611f1b573d600060003e3d6000fd5b505050506040513d6020811015611f325760006000fd5b81019080805190602001909291905050509050611f4a565b90565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633af9e669306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015611ff15760006000fd5b505af1158015612006573d600060003e3d6000fd5b505050506040513d602081101561201d5760006000fd5b81019080805190602001909291905050509050612035565b90565b60006000600060006000606b600050600087815260200190815260200160002060005090508060000160005054945084508060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1693508350806002016000505492508250806003016000505491508150505b9193509193565b6120ca3360726000506122a490919063ffffffff16565b1515612141576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260108152602001807f6d75737420626520616e2061646d696e0000000000000000000000000000000081526020015060200191505060405180910390fd5b607360009054906101000a900460ff161515156121c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f636f6e747261637420697320706175736564000000000000000000000000000081526020015060200191505060405180910390fd5b60006121d961187e63ffffffff16565b141515612251576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f7468657265206973206120636f6d6d697474656420647261770000000000000081526020015060200191505060405180910390fd5b6000612261610cdb63ffffffff16565b141515612277576122766134da63ffffffff16565b5b6122868161351d63ffffffff16565b5b5b5b50565b6000606c6000506005016000505490506122a1565b90565b6000600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141515156122e35760006000fd5b8260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905061233c565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415151561237f5760006000fd5b61238f82826122a463ffffffff16565b151561239b5760006000fd5b60008260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b5050565b600081111515612478576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f6465706f736974206973206e6f742067726561746572207468616e207a65726f81526020015060200191505060405180910390fd5b61248661374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff166323b872dd3330846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050602060405180830381600087803b1580156125415760006000fd5b505af1158015612556573d600060003e3d6000fd5b505050506040513d602081101561256d5760006000fd5b810190808051906020019092919050505015156125f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f746f6b656e207472616e73666572206661696c6564000000000000000000000081526020015060200191505060405180910390fd5b61264d81606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000508190909055506126b0816069600050546137fc90919063ffffffff16565b60696000508190909055506126c961374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b3606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156127725760006000fd5b505af1158015612787573d600060003e3d6000fd5b505050506040513d602081101561279e5760006000fd5b81019080805190602001909291905050501515612806576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180613be36024913960400191505060405180910390fd5b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a0712d68836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561287e5760006000fd5b505af1158015612893573d600060003e3d6000fd5b505050506040513d60208110156128aa5760006000fd5b8101908080519060200190929190505050141515612933576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f636f756c64206e6f7420737570706c79206d6f6e6579206d61726b657400000081526020015060200191505060405180910390fd5b5b50565b600081111515612992576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180613b926023913960400191505060405180910390fd5b6129aa8160696000505461382590919063ffffffff16565b60696000508190909055506000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663852a12e3836040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015612a2d5760006000fd5b505af1158015612a42573d600060003e3d6000fd5b505050506040513d6020811015612a595760006000fd5b8101908080519060200190929190505050141515612ae2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f636f756c64206e6f742072656465656d2066726f6d20636f6d706f756e64000081526020015060200191505060405180910390fd5b612af061374a63ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b158015612b775760006000fd5b505af1158015612b8c573d600060003e3d6000fd5b505050506040513d6020811015612ba35760006000fd5b81019080805190602001909291905050501515612c2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f636f756c64206e6f74207472616e736665722077696e6e696e6773000000000081526020015060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5826040518082815260200191505060405180910390a25b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151515612d25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f62656e65666963696172792073686f756c64206e6f742062652030783000000081526020015060200191505060405180910390fd5b80606760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f4adde74fa6a2bac1c22b89b0488eb67527c033fc6110f443d1424a91a0d41d4560405160405180910390a25b50565b60006000303b905060008114915050612dc256505b90565b612ddc81607260005061384e90919063ffffffff16565b8073ffffffffffffffffffffffffffffffffffffffff167f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33960405160405180910390a25b50565b60008110151515612e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f666565206d757374206265207a65726f206f722067726561746572000000000081526020015060200191505060405180910390fd5b670de0b6b3a76400008111151515612f22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f666565206672616374696f6e206d7573742062652031206f72206c657373000081526020015060200191505060405180910390fd5b8060686000508190909055507f19cbde830537adec39ff348fcf33c89911750be4bc7433a01b3836d71ddb7881816040518082815260200191505060405180910390a15b50565b60006000831415612f7d5760009050612fa8565b60008284029050828482811515612f9057fe5b04141515612f9e5760006000fd5b80915050612fa856505b92915050565b6000612fbe61187e63ffffffff16565b90506000606b60005060008381526020019081526020016000206000509050838360405160200180836000191660001916815260200182600019166000191681526020019250505060405160208183030381529060405280519060200120600019168160030160005054600019161415156130a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f73656372657420646f6573206e6f74206d61746368000000000000000000000081526020015060200191505060405180910390fd5b60006130b4611f4d63ffffffff16565b905060006130d06069600050548361382590919063ffffffff16565b9050600083600201600050548260405160200180838152602001828152602001925050506040516020818303038152906040528051906020012087189050600061311f82611dea63ffffffff16565b9050600061313a86600001600050548561390a63ffffffff16565b90506131b881606a60005060008960010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060008860010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550600061323c828661382590919063ffffffff16565b9050600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415801561327c575060008114155b156133e0576132d981606a60005060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050546137fc90919063ffffffff16565b606a60005060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819090905550606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce8063e3451b76909185846040518463ffffffff1660e01b8152600401808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060006040518083038186803b1580156133b65760006000fd5b505af41580156133cb573d600060003e3d6000fd5b50505050856069600050819090905550613404565b6133f8826069600050546137fc90919063ffffffff16565b60696000508190909055505b606b6000506000898152602001908152602001600020600060008201600050600090556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160005060009055600382016000506000905550508273ffffffffffffffffffffffffffffffffffffffff16887f39d270b67baa0bff7a394d3427e52a85d706cae15e649754ec7b54f3c9deb3f0868486604051808460001916600019168152602001838152602001828152602001935050505060405180910390a350505050505050505b5050565b60006134ea610cdb63ffffffff16565b9050807f023ad9f3cfd45bbf91919354cab651602c11b3d4267df2f095331f1e31c0c42960405160405180910390a2505b565b606c60005073d215cf8d8bc151414a9c5c145fe219e746e5ce80636290728890916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156135735760006000fd5b505af4158015613588573d600060003e3d6000fd5b505050506040513d602081101561359f5760006000fd5b81019080805190602001909291905050505060405180608001604052806068600050548152602001606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020014381526020018260001916815260200150606b6000506000606c60005060040160005054815260200190815260200160002060005060008201518160000160005090905560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160005090905560608201518160030160005090600019169055905050606760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16606c600050600401600050547f3ba93e35d4f024f23249948504642bc624ab65bc80542daab33f8583f1b8d72f836068600050546040518083600019166000191681526020018281526020019250505060405180910390a35b50565b6000606660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b815260040160206040518083038186803b1580156137b55760006000fd5b505afa1580156137ca573d600060003e3d6000fd5b505050506040513d60208110156137e15760006000fd5b810190808051906020019092919050505090506137f9565b90565b6000600082840190508381101515156138155760006000fd5b8091505061381f56505b92915050565b60008282111515156138375760006000fd5b600082840390508091505061384856505b92915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415151561388b5760006000fd5b61389b82826122a463ffffffff16565b1515156138a85760006000fd5b60018260000160005060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b5050565b60006000735ce0678e90719ff5382a57fa693394aee468f11b63bd5cbd62846040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561395e5760006000fd5b505af4158015613973573d600060003e3d6000fd5b505050506040513d602081101561398a5760006000fd5b810190808051906020019092919050505090506000735ce0678e90719ff5382a57fa693394aee468f11b633c4308a883735ce0678e90719ff5382a57fa693394aee468f11b63d6c1528b8960126040518363ffffffff1660e01b8152600401808381526020018260ff1660ff1681526020019250505060206040518083038186803b158015613a195760006000fd5b505af4158015613a2e573d600060003e3d6000fd5b505050506040513d6020811015613a455760006000fd5b81019080805190602001909291905050506040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b158015613a925760006000fd5b505af4158015613aa7573d600060003e3d6000fd5b505050506040513d6020811015613abe5760006000fd5b81019080805190602001909291905050509050735ce0678e90719ff5382a57fa693394aee468f11b630e998993826040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613b215760006000fd5b505af4158015613b36573d600060003e3d6000fd5b505050506040513d6020811015613b4d5760006000fd5b810190808051906020019092919050505092505050613b695650505b9291505056fe62616c616e63652068617320616c7265616479206265656e2077697468647261776e7769746864726177616c206973206e6f742067726561746572207468616e207a65726f436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a6564636f756c64206e6f7420617070726f7665206d6f6e6579206d61726b6574207370656e64a265627a7a723058204f56aea8ae7585b4254f75cd16f60cfe6d95ca3c3081d15b87c30f009647b05964736f6c634300050a0032

Deployed Bytecode Sourcemap

2420:19965:12:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2420:19965:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5760:31;;;:::i;:::-;;;;;;;;;;;;;;;;;;;5682:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;21169:175;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;21169:175:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;13196:264;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;13196:264:12;;;;;;;;;;;;;;;;;:::i;:::-;;20821:96;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;20821:96:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;15401:100;;;:::i;:::-;;;;;;;;;;;;;;;;;;;14393:316;;;:::i;:::-;;20008:123;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;20008:123:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;21909:104;;;:::i;:::-;;12605:184;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;12605:184:12;;;;;;;;;;;;;;;;;:::i;:::-;;18348:92;;;:::i;:::-;;;;;;;;;;;;;;;;;;;6745:416;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;6745:416:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;17166:116;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;17166:116:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;6236:18;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;5489:21;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;20558:79;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;20558:79:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;17498:97;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;17498:97:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;18684:129;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;18684:129:12;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;16776:126;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;16776:126:12;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;15619:185;;;:::i;:::-;;;;;;;;;;;;;;;;;;;466:214:8;;;:::i;:::-;;8819:273:12;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;8819:273:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;21804:101;;;:::i;:::-;;19401:111;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;19401:111:12;;;;;;;;;;;;;;;;;:::i;:::-;;5581:33;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;17799:126;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;17799:126:12;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;18980:105;;;:::i;:::-;;;;;;;;;;;;;;;;;;;21697:103;;;:::i;:::-;;;;;;;;;;;;;;;;;;;16168:354;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;16168:354:12;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8147:238;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;8147:238:12;;;;;;;;;;;;;;;;;;;;:::i;:::-;;18083:100;;;:::i;:::-;;;;;;;;;;;;;;;;;;;5760:31;;;;;;:::o;5682:30::-;;;;;;:::o;21169:175::-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21237:18;21248:6;21237;;;:10;;:18;;;;:::i;:::-;21229:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21286:21;21300:6;21286;;;:13;;:21;;;;:::i;:::-;21332:6;21319:20;;;;;;;;;;;;22101:1;21169:175;;:::o;13196:264::-;22175:1;22152:19;:17;:19;;:::i;:::-;:24;;22144:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1106:1:8;1089:13;;:18;;;;;;;;;;;;;;;1117:20;1140:13;;;;1117:36;;22342:6:12;;;;;;;;;;;22341:7;22333:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13326:9;;;:17;;;;13344:10;13356:7;13326:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13326:38:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;13326:38:12;;;;13396:17;13405:7;13396:8;:17;;:::i;:::-;13435:10;13425:30;;;13447:7;13425:30;;;;;;;;;;;;;;;;;;22377:1;1163::8;1198:13;;;;1182:12;:29;1174:38;;;;;;;;22208:1:12;;13196:264;;:::o;20821:96::-;20875:4;20894:18;20905:6;20894;;;:10;;:18;;;;:::i;:::-;20887:25;;;;20821:96;;;;:::o;15401:100::-;15451:7;15473:9;;;:23;;;;;15466:30;;;;15401:100;;:::o;14393:316::-;1106:1:8;1089:13;;:18;;;;;;;;;;;;;;;1117:20;1140:13;;;;1117:36;;14439:12:12;14454:8;;;:20;14463:10;14454:20;;;;;;;;;;;;;;;;;;14439:35;;14499:1;14489:7;:11;14481:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14602:1;14579:8;;;:20;14588:10;14579:20;;;;;;;;;;;;;;;;:24;;;;;;;14649:9;;;:18;;;;14668:10;14649:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;14649:30:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;14649:30:12;;;;14686:18;14696:7;14686:9;:18;;:::i;:::-;1163:1:8;;1198:13;;;;1182:12;:29;1174:38;;;;;;;;14393:316:12;;:::o;20008:123::-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20087:39;20110:15;20087:22;:39;;:::i;:::-;22101:1;20008:123;;:::o;21909:104::-;22254:6;;;;;;;;;;;22246:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21971:5;21962:6;;:14;;;;;;;;;;;;;;;;;;21997:10;21988:20;;;;;;;;;;;;22101:1;22293;21909:104;:::o;12605:184::-;1106:1:8;1089:13;;:18;;;;;;;;;;;;;;;1117:20;1140:13;;;;1117:36;;22342:6:12;;;;;;;;;;;22341:7;22333:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12714:17;12723:7;12714:8;:17;;:::i;:::-;12764:10;12743:41;;;12776:7;12743:41;;;;;;;;;;;;;;;;;;22377:1;1163::8;1198:13;;;;1182:12;:29;1174:38;;;;;;;;12605:184:12;;;:::o;18348:92::-;18391:7;18413:9;;;:20;;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;18413:22:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18413:22:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;18413:22:12;;;;;;;;;;;;;;;;18406:29;;;;18348:92;;:::o;6745:416::-;1024:12:9;;;;;;;;;;;:31;;;;1040:15;:13;:15;;:::i;:::-;1024:31;:47;;;;1060:11;;;;;;;;;;;1059:12;1024:47;1016:106;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1129:19;1152:12;;;;;;;;;;;1151:13;1129:35;;1174:14;1170:80;;;1213:4;1198:12;;:19;;;;;;;;;;;;;;;;;;1239:4;1225:11;;:18;;;;;;;;;;;;;;;;;;1170:80;6911:1:12;6893:20;;:6;:20;;;;6885:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6983:1;6964:21;;:7;:21;;;;6956:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7041:7;7024:6;;:25;;;;;;;;;;;;;;;;;;7055:17;7065:6;7055:9;:17;;:::i;:::-;7078:33;7098:12;7078:19;:33;;:::i;:::-;7117:39;7140:15;7117:22;:39;;:::i;:::-;1256:1:9;1268:14;1264:55;;;1307:5;1292:12;;:20;;;;;;;;;;;;;;;;;;1264:55;6745:416:12;;;;;;:::o;17166:116::-;17225:7;17247:9;;;:23;;;;17271:5;17247:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17247:30:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;17247:30:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;17247:30:12;;;;;;;;;;;;;;;;17240:37;;;;17166:116;;;;:::o;6236:18::-;;;;;;;;;;;;;:::o;5489:21::-;;;;;;;;;;;;;:::o;20558:79::-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20615:17;20625:6;20615:9;:17;;:::i;:::-;22101:1;20558:79;;:::o;17498:97::-;17553:7;17575:8;;;:15;17584:5;17575:15;;;;;;;;;;;;;;;;;;17568:22;;;;17498:97;;;;:::o;18684:129::-;18753:7;18775:33;18800:7;18775:20;:18;:20;;:::i;:::-;:24;;:33;;;;:::i;:::-;18768:40;;;;18684:129;;;;:::o;16776:126::-;16840:7;16862:9;;;:28;;;;16891:5;16862:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;16862:35:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;16862:35:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;16862:35:12;;;;;;;;;;;;;;;;16855:42;;;;16776:126;;;;:::o;15619:185::-;15674:7;15719:1;15693:9;;;:23;;;;;:27;15689:111;;;15763:1;15737:9;;;:23;;;;;:27;15730:34;;;;15689:111;;;15792:1;15785:8;;;;15689:111;15619:185;;:::o;466:214:8:-;1024:12:9;;;;;;;;;;;:31;;;;1040:15;:13;:15;;:::i;:::-;1024:31;:47;;;;1060:11;;;;;;;;;;;1059:12;1024:47;1016:106;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1129:19;1152:12;;;;;;;;;;;1151:13;1129:35;;1174:14;1170:80;;;1213:4;1198:12;;:19;;;;;;;;;;;;;;;;;;1239:4;1225:11;;:18;;;;;;;;;;;;;;;;;;1170:80;672:1:8;656:13;;:17;;;;;;;1256:1:9;1268:14;1264:55;;;1307:5;1292:12;;:20;;;;;;;;;;;;;;;;;;1264:55;466:214:8;;:::o;8819:273:12:-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22342:6;;;;;;;;;;;22341:7;22333:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8981:1;8953:24;:22;:24;;:::i;:::-;:29;;8945:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9022:25;9029:10;9041:5;9022:6;:25;;:::i;:::-;9053:8;:6;:8;;:::i;:::-;9067:20;9072:14;9067:4;:20;;:::i;:::-;22377:1;22101;8819:273;;;;:::o;21804:101::-;22342:6;;;;;;;;;;;22341:7;22333:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21866:4;21857:6;;:13;;;;;;;;;;;;;;;;;;21889:10;21882:18;;;;;;;;;;;;22101:1;22377;21804:101;:::o;19401:111::-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19474:33;19494:12;19474:19;:33;;:::i;:::-;22101:1;19401:111;;:::o;5581:33::-;;;;;;;;;;;;;:::o;17799:126::-;17863:7;17885:9;;;:25;;;;17911:8;17885:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17885:35:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;17885:35:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;17885:35:12;;;;;;;;;;;;;;;;17878:42;;;;17799:126;;;;:::o;18980:105::-;19031:7;19053:6;;;;;;;;;;;:25;;;:27;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;19053:27:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19053:27:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;19053:27:12;;;;;;;;;;;;;;;;19046:34;;;;18980:105;;:::o;21697:103::-;21732:7;21754:6;;;;;;;;;;;:26;;;21789:4;21754:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;21754:41:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;21754:41:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;21754:41:12;;;;;;;;;;;;;;;;21747:48;;;;21697:103;;:::o;16168:354::-;16228:19;16253:22;16281:19;16306:18;16335:17;16355:5;;;:14;16361:7;16355:14;;;;;;;;;;;;;16335:34;;16389:4;:16;;;;;16375:30;;;;16428:4;:19;;;;;;;;;;;;16411:36;;;;16467:4;:16;;;;;16453:30;;;;16502:4;:15;;;;;16489:28;;;;16168:354;;;;;;;:::o;8147:238::-;22052:22;22063:10;22052:6;;;:10;;:22;;;;:::i;:::-;22044:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22342:6;;;;;;;;;;;22341:7;22333:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8265:1;8237:24;:22;:24;;:::i;:::-;:29;8229:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8329:1;8306:19;:17;:19;;:::i;:::-;:24;;8302:53;;;8340:8;:6;:8;;:::i;:::-;8302:53;8360:20;8365:14;8360:4;:20;;:::i;:::-;22377:1;22101;8147:238;;:::o;18083:100::-;18131:7;18153:9;;;:25;;;;;18146:32;;;;18083:100;;:::o;786:162:1:-;858:4;901:1;882:21;;:7;:21;;;;874:30;;;;;;;;921:4;:11;;;;:20;933:7;921:20;;;;;;;;;;;;;;;;;;;;;;;;;914:27;;;;786:162;;;;;:::o;514:184::-;612:1;593:21;;:7;:21;;;;585:30;;;;;;;;633:18;637:4;643:7;633:3;:18;;:::i;:::-;625:27;;;;;;;;686:5;663:4;:11;;;;:20;675:7;663:20;;;;;;;;;;;;;;;;:28;;;;;;;;;;;;;;;;;;514:184;;;:::o;13668:645:12:-;13736:1;13726:7;:11;13718:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13835:7;:5;:7;;:::i;:::-;:20;;;13856:10;13876:4;13883:7;13835:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13835:56:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;13835:56:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;13835:56:12;;;;;;;;;;;;;;;;13827:90;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13980:33;14005:7;13980:8;;;:20;13989:10;13980:20;;;;;;;;;;;;;;;;;;:24;;:33;;;;:::i;:::-;13957:8;;;:20;13966:10;13957:20;;;;;;;;;;;;;;;;:56;;;;;;;14080:29;14101:7;14080:16;;;;:20;;:29;;;;:::i;:::-;14061:16;;:48;;;;;;;14153:7;:5;:7;;:::i;:::-;:15;;;14177:6;;;;;;;;;;;14186:7;14153:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;14153:41:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;14153:41:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;14153:41:12;;;;;;;;;;;;;;;;14145:90;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14273:1;14249:6;;;;;;;;;;;:11;;;14261:7;14249:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;14249:20:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;14249:20:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;14249:20:12;;;;;;;;;;;;;;;;:25;14241:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13668:645;;:::o;14827:466::-;14896:1;14886:7;:11;14878:59;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15004:29;15025:7;15004:16;;;;:20;;:29;;;;:::i;:::-;14985:16;;:48;;;;;;;15127:1;15091:6;;;;;;;;;;;:23;;;15115:7;15091:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;15091:32:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;15091:32:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;15091:32:12;;;;;;;;;;;;;;;;:37;15083:80;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15177:7;:5;:7;;:::i;:::-;:16;;;15194:10;15206:7;15177:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;15177:37:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;15177:37:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;15177:37:12;;;;;;;;;;;;;;;;15169:77;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15268:10;15258:30;;;15280:7;15258:30;;;;;;;;;;;;;;;;;;14827:466;;:::o;20135:244::-;20242:1;20215:29;;:15;:29;;;;20207:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20305:15;20284:18;;:36;;;;;;;;;;;;;;;;;;20358:15;20332:42;;;;;;;;;;;;20135:244;;:::o;1409:467:9:-;1456:4;1797:10;1842:7;1830:20;1824:26;;1870:1;1864:2;:7;1857:14;;;;;1409:467;;;:::o;20921:103:12:-;20971:18;20982:6;20971;;;:10;;:18;;;;:::i;:::-;21012:6;21001:18;;;;;;;;;;;;20921:103;;:::o;19516:289::-;19606:1;19590:12;:17;;19582:57;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2616:19;19653:12;:28;;19645:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19740:12;19722:15;;:30;;;;;;;19764:36;19787:12;19764:36;;;;;;;;;;;;;;;;;;19516:289;;:::o;231:421:3:-;289:7;534:1;529;:6;525:45;;;558:1;551:8;;;;525:45;580:9;596:1;592;:5;580:17;;624:1;619;615;:5;;;;;;;;:10;607:19;;;;;;;;644:1;637:8;;;;;231:421;;;;;;:::o;9809:1717:12:-;9872:14;9889:24;:22;:24;;:::i;:::-;9872:41;;9919:17;9939:5;;;:13;9945:6;9939:13;;;;;;;;;;;;;9919:33;;10013:7;10022:5;9996:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;9996:32:12;;;9986:43;;;;;;9967:62;;;:4;:15;;;;;:62;;;;9959:96;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10098:25;10126:9;:7;:9;;:::i;:::-;10098:37;;10141:21;10165:39;10187:16;;;;10165:17;:21;;:39;;;;:::i;:::-;10141:63;;10309:15;10364:4;:16;;;;;10382:13;10347:49;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;10347::12;;;10337:60;;;;;;10327:7;:70;10309:88;;10455:22;10480:24;10496:7;10480:15;:24;;:::i;:::-;10455:49;;10548:11;10562:45;10575:4;:16;;;;;10593:13;10562:12;:45;;:::i;:::-;10548:59;;10687:38;10721:3;10687:8;;;:29;10696:4;:19;;;;;;;;;;;;10687:29;;;;;;;;;;;;;;;;;;:33;;:38;;;;:::i;:::-;10655:8;;;:29;10664:4;:19;;;;;;;;;;;;10655:29;;;;;;;;;;;;;;;;:70;;;;;;;10766:19;10788:22;10806:3;10788:13;:17;;:22;;;;:::i;:::-;10766:44;;10911:1;10885:28;;:14;:28;;;;:48;;;;;10932:1;10917:11;:16;;10885:48;10881:460;;;11008:41;11037:11;11008:8;;;:24;11017:14;11008:24;;;;;;;;;;;;;;;;;;:28;;:41;;;;:::i;:::-;10981:8;;;:24;10990:14;10981:24;;;;;;;;;;;;;;;;:68;;;;;;;11107:9;;;:17;;;;11125:14;11141:11;11107:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11107:46:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;11107:46:12;;;;11218:17;11199:16;;:36;;;;;;;10881:460;;;11309:25;11330:3;11309:16;;;;:20;;:25;;;;:::i;:::-;11290:16;;:44;;;;;;;10881:460;11401:5;;;:13;11407:6;11401:13;;;;;;;;;;;;11394:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11456:14;11426:95;;11442:6;11426:95;11478:7;11493:11;11512:3;11426:95;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9809:1717;;;;;;;;;;;:::o;7634:102::-;7667:14;7684:19;:17;:19;;:::i;:::-;7667:36;;7724:6;7714:17;;;;;;;;;;7634:102;;:::o;7271:308::-;7321:9;;;:22;;;;:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;7321:24:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7321:24:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;7321:24:12;;;;;;;;;;;;;;;;;7384:68;;;;;;;;7389:15;;;;7384:68;;;;7406:18;;;;;;;;;;;7384:68;;;;;;7426:12;7384:68;;;;7440:11;7384:68;;;;;;;;7351:5;;;:30;7357:9;;;:23;;;;;7351:30;;;;;;;;;;;;:101;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7508:18;;;;;;;;;;;7463:111;;7477:9;;;:23;;;;;7463:111;7534:11;7553:15;;;;7463:111;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7271:308;;:::o;21450:93::-;21490:6;21518;;;;;;;;;;;:17;;;:19;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;21518:19:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;21518:19:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;21518:19:12;;;;;;;;;;;;;;;;21504:34;;;;21450:93;;:::o;1439:145:3:-;1497:7;1516:9;1532:1;1528;:5;1516:17;;1556:1;1551;:6;;1543:15;;;;;;;;1576:1;1569:8;;;;;1439:145;;;;;;:::o;1211:::-;1269:7;1301:1;1296;:6;;1288:15;;;;;;;;1313:9;1329:1;1325;:5;1313:17;;1348:1;1341:8;;;;;1211:145;;;;;;:::o;259:181:1:-;354:1;335:21;;:7;:21;;;;327:30;;;;;;;;376:18;380:4;386:7;376:3;:18;;:::i;:::-;375:19;367:28;;;;;;;;429:4;406;:11;;;;:20;418:7;406:20;;;;;;;;;;;;;;;;:27;;;;;;;;;;;;;;;;;;259:181;;;:::o;11804:355:12:-;11895:7;11910:25;11938:11;:20;11966:14;11938:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;11938:44:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;11938:44:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;11938:44:12;;;;;;;;;;;;;;;;11910:72;;11988:15;12006:11;:20;12027:18;12047:11;:20;12075:12;12096:2;12047:53;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12047:53:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;12047:53:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;12047:53:12;;;;;;;;;;;;;;;;12006:95;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12006:95:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;12006:95:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;12006:95:12;;;;;;;;;;;;;;;;11988:113;;12122:11;:21;12144:8;12122:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12122:31:12;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;12122:31:12;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;12122:31:12;;;;;;;;;;;;;;;;12107:47;;;;;;11804:355;;;;;;;:::o

Swarm Source

bzzr://4f56aea8ae7585b4254f75cd16f60cfe6d95ca3c3081d15b87c30f009647b059

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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