Address Details
contract

0xa3D754445f8C3a8134F64467826352885558FD65

Contract Name
BiPoolManager
Creator
0x56fd3f–9b8d81 at 0xc5efbb–feaae0
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
1 Transactions
Transfers
0 Transfers
Gas Used
28,897
Last Balance Update
26870806
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
BiPoolManager




Optimization enabled
true
Compiler version
v0.5.17+commit.d19bba13




Optimization runs
10000
Verified at
2024-01-10T17:39:26.940087Z

lib/mento-core-2.2.0/contracts/swap/BiPoolManager.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

import { Ownable } from "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import { SafeMath } from "openzeppelin-solidity/contracts/math/SafeMath.sol";

import { IERC20Metadata } from "../common/interfaces/IERC20Metadata.sol";
import { IExchangeProvider } from "../interfaces/IExchangeProvider.sol";
import { IBiPoolManager } from "../interfaces/IBiPoolManager.sol";
import { IReserve } from "../interfaces/IReserve.sol";
import { IPricingModule } from "../interfaces/IPricingModule.sol";
import { ISortedOracles } from "../interfaces/ISortedOracles.sol";
import { IBreakerBox } from "../interfaces/IBreakerBox.sol";

import { Initializable } from "../common/Initializable.sol";
import { FixidityLib } from "../common/FixidityLib.sol";

/**
 * @title BiPoolExchangeManager
 * @notice An exchange manager that manages asset exchanges consisting of two assets
 */
contract BiPoolManager is IExchangeProvider, IBiPoolManager, Initializable, Ownable {
  using FixidityLib for FixidityLib.Fraction;
  using SafeMath for uint256;

  /* ==================== State Variables ==================== */

  // Address of the broker contract.
  address public broker;

  // Maps an exchange id to the corresponding PoolExchange struct.
  // exchangeId is in the format "asset0Symbol:asset1Symbol:pricingModuleName"
  mapping(bytes32 => PoolExchange) public exchanges;
  bytes32[] public exchangeIds;

  uint256 private constant TRADING_MODE_BIDIRECTIONAL = 0;

  // Address of the Mento Reserve contract
  IReserve public reserve;

  // Address of the Mento BreakerBox contract
  IBreakerBox public breakerBox;

  // Address of the Mento SortedOracles contract
  ISortedOracles public sortedOracles;

  // Token precision multiplier used to normalize values to the
  // same precision when calculating vAMM bucket sizes.
  mapping(address => uint256) public tokenPrecisionMultipliers;

  bytes32 public constant CONSTANT_SUM = keccak256(abi.encodePacked("ConstantSum"));
  bytes32 public constant CONSTANT_PRODUCT = keccak256(abi.encodePacked("ConstantProduct"));

  // Maps a pricing module identifier to the address of the pricing module contract.
  mapping(bytes32 => address) public pricingModules;

  /* ==================== Constructor ==================== */

  /**
   * @notice Sets initialized == true on implementation contracts.
   * @param test Set to true to skip implementation initialization.
   */
  // solhint-disable-next-line no-empty-blocks
  constructor(bool test) public Initializable(test) {}

  /**
   * @notice Allows the contract to be upgradable via the proxy.
   * @param _broker The address of the broker contract.
   * @param _reserve The address of the reserve contract.
   * @param _sortedOracles The address of the sorted oracles contract.
   * @param _breakerBox The address of the breaker box contract.
   */
  function initialize(
    address _broker,
    IReserve _reserve,
    ISortedOracles _sortedOracles,
    IBreakerBox _breakerBox
  ) external initializer {
    _transferOwnership(msg.sender);
    setBroker(_broker);
    setReserve(_reserve);
    setSortedOracles(_sortedOracles);
    setBreakerBox(_breakerBox);
  }

  /* ==================== Modifiers ==================== */

  modifier onlyBroker() {
    require(msg.sender == broker, "Caller is not the Broker");
    _;
  }

  modifier verifyExchangeTokens(
    address tokenIn,
    address tokenOut,
    PoolExchange memory exchange
  ) {
    require(
      (tokenIn == exchange.asset0 && tokenOut == exchange.asset1) ||
        (tokenIn == exchange.asset1 && tokenOut == exchange.asset0),
      "tokenIn and tokenOut must match exchange"
    );
    _;
  }

  /* ==================== View Functions ==================== */

  /**
   * @notice Get a PoolExchange from storage.
   * @param exchangeId the exchange id
   */
  function getPoolExchange(bytes32 exchangeId) public view returns (PoolExchange memory exchange) {
    exchange = exchanges[exchangeId];
    require(exchange.asset0 != address(0), "An exchange with the specified id does not exist");
  }

  /**
   * @notice Get all exchange IDs.
   * @return exchangeIds List of the exchangeIds.
   */
  function getExchangeIds() external view returns (bytes32[] memory) {
    return exchangeIds;
  }

  /**
   * @notice Get all exchanges (used by interfaces)
   * @dev We don't expect the number of exchanges to grow to
   * astronomical values so this is safe gas-wise as is.
   */
  function getExchanges() public view returns (Exchange[] memory _exchanges) {
    uint256 numExchanges = exchangeIds.length;
    _exchanges = new Exchange[](numExchanges);
    for (uint256 i = 0; i < numExchanges; i++) {
      _exchanges[i].exchangeId = exchangeIds[i];
      _exchanges[i].assets = new address[](2);
      _exchanges[i].assets[0] = exchanges[exchangeIds[i]].asset0;
      _exchanges[i].assets[1] = exchanges[exchangeIds[i]].asset1;
    }
  }

  /**
   * @notice Calculate amountOut of tokenOut received for a given amountIn of tokenIn
   * @param exchangeId The id of the exchange i.e PoolExchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountIn The amount of tokenIn to be sold
   * @return amountOut The amount of tokenOut to be bought
   */
  function getAmountOut(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external view returns (uint256 amountOut) {
    PoolExchange memory exchange = getPoolExchange(exchangeId);
    uint256 scaledAmountIn = amountIn.mul(tokenPrecisionMultipliers[tokenIn]);
    (uint256 scaledAmountOut, ) = _getAmountOut(exchange, tokenIn, tokenOut, scaledAmountIn);
    amountOut = scaledAmountOut.div(tokenPrecisionMultipliers[tokenOut]);
    return amountOut;
  }

  /**
   * @notice Calculate amountIn of tokenIn for a given amountOut of tokenOut
   * @param exchangeId The id of the exchange i.e PoolExchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountOut The amount of tokenOut to be bought
   * @return amountIn The amount of tokenIn to be sold
   */
  function getAmountIn(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external view returns (uint256 amountIn) {
    PoolExchange memory exchange = getPoolExchange(exchangeId);
    uint256 scaledAmountOut = amountOut.mul(tokenPrecisionMultipliers[tokenOut]);
    (uint256 scaledAmountIn, ) = _getAmountIn(exchange, tokenIn, tokenOut, scaledAmountOut);
    amountIn = scaledAmountIn.div(tokenPrecisionMultipliers[tokenIn]);
    return amountIn;
  }

  /* ==================== Mutative Functions ==================== */

  /**
   * @notice Sets the address of the broker contract.
   * @param _broker The new address of the broker contract.
   */
  function setBroker(address _broker) public onlyOwner {
    require(_broker != address(0), "Broker address must be set");
    broker = _broker;
    emit BrokerUpdated(_broker);
  }

  /**
   * @notice Sets the address of the reserve contract.
   * @param _reserve The new address of the reserve contract.
   */
  function setReserve(IReserve _reserve) public onlyOwner {
    require(address(_reserve) != address(0), "Reserve address must be set");
    reserve = _reserve;
    emit ReserveUpdated(address(_reserve));
  }

  /**
   * @notice Sets the address of the BreakerBox.
   * @param _breakerBox The new BreakerBox address.
   */
  function setBreakerBox(IBreakerBox _breakerBox) public onlyOwner {
    require(address(_breakerBox) != address(0), "BreakerBox address must be set");
    breakerBox = _breakerBox;
    emit BreakerBoxUpdated(address(_breakerBox));
  }

  /**
   * @notice Sets the address of the sortedOracles contract.
   * @param _sortedOracles The new address of the sorted oracles contract.
   */
  function setSortedOracles(ISortedOracles _sortedOracles) public onlyOwner {
    require(address(_sortedOracles) != address(0), "SortedOracles address must be set");
    sortedOracles = _sortedOracles;
    emit SortedOraclesUpdated(address(_sortedOracles));
  }

  /**
   * @notice Updates the pricing modules for a list of identifiers
   * @dev This function can only be called by the owner of the contract.
   *      The number of identifiers and modules provided must be the same.
   * @param identifiers An array of identifiers for which the pricing modules are to be set.
   * @param modules An array of module addresses corresponding to each identifier.
   */
  function setPricingModules(bytes32[] calldata identifiers, address[] calldata modules) external onlyOwner {
    require(identifiers.length == modules.length, "identifiers and modules must be the same length");
    for (uint256 i = 0; i < identifiers.length; i++) {
      pricingModules[identifiers[i]] = modules[i];
    }
    emit PricingModulesUpdated(identifiers, modules);
  }

  /**
   * @notice Creates a new exchange using the given parameters.
   * @param _exchange the PoolExchange to create.
   * @return exchangeId The id of the newly created exchange.
   */
  function createExchange(PoolExchange calldata _exchange) external onlyOwner returns (bytes32 exchangeId) {
    PoolExchange memory exchange = _exchange;
    require(address(exchange.pricingModule) != address(0), "pricingModule must be set");
    require(exchange.asset0 != address(0), "asset0 must be set");
    require(exchange.asset1 != address(0), "asset1 must be set");
    require(exchange.asset0 != exchange.asset1, "exchange assets can't be identical");
    require(
      pricingModules[pricingModuleIdentifier(exchange)] == address(exchange.pricingModule),
      "invalid pricingModule"
    );

    exchangeId = keccak256(
      abi.encodePacked(
        IERC20Metadata(exchange.asset0).symbol(),
        IERC20Metadata(exchange.asset1).symbol(),
        exchange.pricingModule.name()
      )
    );
    require(exchanges[exchangeId].asset0 == address(0), "An exchange with the specified assets and exchange exists");

    validate(exchange);
    (uint256 bucket0, uint256 bucket1) = getUpdatedBuckets(exchange);

    exchange.bucket0 = bucket0;
    exchange.bucket1 = bucket1;

    uint256 asset0Decimals = IERC20Metadata(exchange.asset0).decimals();
    uint256 asset1Decimals = IERC20Metadata(exchange.asset1).decimals();

    require(asset0Decimals <= 18, "asset0 decimals must be <= 18");
    require(asset1Decimals <= 18, "asset1 decimals must be <= 18");

    tokenPrecisionMultipliers[exchange.asset0] = 10**(18 - uint256(asset0Decimals));
    tokenPrecisionMultipliers[exchange.asset1] = 10**(18 - uint256(asset1Decimals));

    exchanges[exchangeId] = exchange;
    exchangeIds.push(exchangeId);

    emit ExchangeCreated(exchangeId, exchange.asset0, exchange.asset1, address(exchange.pricingModule));
  }

  /**
   * @notice Destroys a exchange with the given parameters if it exists and frees up
   *         the collateral and stable allocation it was using.
   * @param exchangeId the id of the exchange to destroy
   * @param exchangeIdIndex The index of the exchangeId in the ids array
   * @return destroyed A boolean indicating whether or not the exchange was successfully destroyed.
   */
  function destroyExchange(bytes32 exchangeId, uint256 exchangeIdIndex) external onlyOwner returns (bool destroyed) {
    require(exchangeIdIndex < exchangeIds.length, "exchangeIdIndex not in range");
    require(exchangeIds[exchangeIdIndex] == exchangeId, "exchangeId at index doesn't match");
    PoolExchange memory exchange = exchanges[exchangeId];

    delete exchanges[exchangeId];
    exchangeIds[exchangeIdIndex] = exchangeIds[exchangeIds.length.sub(1)];
    exchangeIds.pop();
    destroyed = true;

    emit ExchangeDestroyed(exchangeId, exchange.asset0, exchange.asset1, address(exchange.pricingModule));
  }

  /**
   * @notice Execute a token swap with fixed amountIn
   * @param exchangeId The id of exchange, i.e. PoolExchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountIn The amount of tokenIn to be sold
   * @return amountOut The amount of tokenOut to be bought
   */
  function swapIn(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external onlyBroker returns (uint256 amountOut) {
    PoolExchange memory exchange = getPoolExchange(exchangeId);
    require(
      breakerBox.getRateFeedTradingMode(exchange.config.referenceRateFeedID) == TRADING_MODE_BIDIRECTIONAL,
      "Trading is suspended for this reference rate"
    );

    uint256 scaledAmountIn = amountIn.mul(tokenPrecisionMultipliers[tokenIn]);
    (uint256 scaledAmountOut, bool bucketsUpdated) = _getAmountOut(exchange, tokenIn, tokenOut, scaledAmountIn);
    executeSwap(exchangeId, exchange, tokenIn, scaledAmountIn, scaledAmountOut, bucketsUpdated);

    amountOut = scaledAmountOut.div(tokenPrecisionMultipliers[tokenOut]);
    return amountOut;
  }

  /**
   * @notice Execute a token swap with fixed amountOut
   * @param exchangeId The id of exchange, i.e. PoolExchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountOut The amount of tokenOut to be bought
   * @return amountIn The amount of tokenIn to be sold
   */
  function swapOut(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external onlyBroker returns (uint256 amountIn) {
    PoolExchange memory exchange = getPoolExchange(exchangeId);
    require(
      breakerBox.getRateFeedTradingMode(exchange.config.referenceRateFeedID) == TRADING_MODE_BIDIRECTIONAL,
      "Trading is suspended for this reference rate"
    );

    uint256 scaledAmountOut = amountOut.mul(tokenPrecisionMultipliers[tokenOut]);
    (uint256 scaledAmountIn, bool bucketsUpdated) = _getAmountIn(exchange, tokenIn, tokenOut, scaledAmountOut);
    executeSwap(exchangeId, exchange, tokenIn, scaledAmountIn, scaledAmountOut, bucketsUpdated);

    amountIn = scaledAmountIn.div(tokenPrecisionMultipliers[tokenIn]);
    return amountIn;
  }

  /* ==================== Private Functions ==================== */

  /**
   * @notice Execute a swap against the in memory exchange and write
   *         the new bucket sizes to storage.
   * @dev In constant sum exchanges, the virtual bucket ratio serve as the reference price
   *      and should remain constant between bucket updates.
   *      Thats why the amounts of a swap are only applied for constant product exchanges.
   * @param exchangeId The id of the exchange
   * @param exchange The exchange to operate on
   * @param tokenIn The token to be sold
   * @param scaledAmountIn The amount of tokenIn to be sold scaled to 18 decimals
   * @param scaledAmountOut The amount of tokenOut to be bought scaled to 18 decimals
   * @param bucketsUpdated whether the buckets updated during the swap
   */
  function executeSwap(
    bytes32 exchangeId,
    PoolExchange memory exchange,
    address tokenIn,
    uint256 scaledAmountIn,
    uint256 scaledAmountOut,
    bool bucketsUpdated
  ) internal {
    if (bucketsUpdated) {
      // solhint-disable-next-line not-rely-on-time
      exchanges[exchangeId].lastBucketUpdate = now;
      emit BucketsUpdated(exchangeId, exchange.bucket0, exchange.bucket1);
    }

    if (isConstantProduct(exchange)) {
      if (tokenIn == exchange.asset0) {
        exchange.bucket0 = exchange.bucket0.add(scaledAmountIn);
        exchange.bucket1 = exchange.bucket1.sub(scaledAmountOut);
      } else {
        exchange.bucket0 = exchange.bucket0.sub(scaledAmountOut);
        exchange.bucket1 = exchange.bucket1.add(scaledAmountIn);
      }
    }

    exchanges[exchangeId].bucket0 = exchange.bucket0;
    exchanges[exchangeId].bucket1 = exchange.bucket1;
  }

  /**
   * @notice Calculate amountOut of tokenOut received for a given amountIn of tokenIn
   * @param exchange The exchange to operate on
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param scaledAmountIn The amount of tokenIn to be sold scaled to 18 decimals
   * @return scaledAmountOut The amount of tokenOut to be bought scaled to 18 decimals
   * @return bucketsUpdated Wether the buckets were updated during the quote
   */
  function _getAmountOut(
    PoolExchange memory exchange,
    address tokenIn,
    address tokenOut,
    uint256 scaledAmountIn
  )
    internal
    view
    verifyExchangeTokens(tokenIn, tokenOut, exchange)
    returns (uint256 scaledAmountOut, bool bucketsUpdated)
  {
    (exchange, bucketsUpdated) = updateBucketsIfNecessary(exchange);

    if (tokenIn == exchange.asset0) {
      scaledAmountOut = exchange.pricingModule.getAmountOut(
        exchange.bucket0,
        exchange.bucket1,
        exchange.config.spread.unwrap(),
        scaledAmountIn
      );
    } else {
      scaledAmountOut = exchange.pricingModule.getAmountOut(
        exchange.bucket1,
        exchange.bucket0,
        exchange.config.spread.unwrap(),
        scaledAmountIn
      );
    }
  }

  /**
   * @notice Calculate amountIn of tokenIn for a given amountOut of tokenOut
   * @param exchange The exchange to operate on
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param scaledAmountOut The amount of tokenOut to be bought scaled to 18 decimals
   * @return scaledAmountIn The amount of tokenIn to be sold scaled to 18 decimals
   * @return bucketsUpdated Whether the buckets were updated during the quote
   */
  function _getAmountIn(
    PoolExchange memory exchange,
    address tokenIn,
    address tokenOut,
    uint256 scaledAmountOut
  )
    internal
    view
    verifyExchangeTokens(tokenIn, tokenOut, exchange)
    returns (uint256 scaledAmountIn, bool bucketsUpdated)
  {
    (exchange, bucketsUpdated) = updateBucketsIfNecessary(exchange);

    if (tokenIn == exchange.asset0) {
      scaledAmountIn = exchange.pricingModule.getAmountIn(
        exchange.bucket0,
        exchange.bucket1,
        exchange.config.spread.unwrap(),
        scaledAmountOut
      );
    } else {
      scaledAmountIn = exchange.pricingModule.getAmountIn(
        exchange.bucket1,
        exchange.bucket0,
        exchange.config.spread.unwrap(),
        scaledAmountOut
      );
    }
  }

  /**
   * @notice If conditions are met, update the exchange bucket sizes.
   * @dev This doesn't checkpoint the exchange, just updates the in-memory one
   * so it should be used in a context that then checkpoints the exchange.
   * @param exchange The exchange being updated.
   * @return exchangeAfter The updated exchange.
   */
  function updateBucketsIfNecessary(PoolExchange memory exchange)
    internal
    view
    returns (PoolExchange memory, bool updated)
  {
    if (shouldUpdateBuckets(exchange)) {
      (exchange.bucket0, exchange.bucket1) = getUpdatedBuckets(exchange);
      updated = true;
    }
    return (exchange, updated);
  }

  /**
   * @notice Determine if a exchange's buckets should be updated
   * based on staleness of buckets and oracle rates.
   * @param exchange The PoolExchange.
   * @return shouldUpdate
   */
  function shouldUpdateBuckets(PoolExchange memory exchange) internal view returns (bool) {
    bool hasValidMedian = oracleHasValidMedian(exchange);
    if (isConstantSum(exchange)) {
      require(hasValidMedian, "no valid median");
    }
    // solhint-disable-next-line not-rely-on-time
    bool timePassed = now >= exchange.lastBucketUpdate.add(exchange.config.referenceRateResetFrequency);
    return timePassed && hasValidMedian;
  }

  /**
   * @notice Determine if the median is valid based on the current oracle rates.
   * @param exchange The PoolExchange.
   * @return HasValidMedian.
   */
  function oracleHasValidMedian(PoolExchange memory exchange) internal view returns (bool) {
    // solhint-disable-next-line not-rely-on-time
    (bool isReportExpired, ) = sortedOracles.isOldestReportExpired(exchange.config.referenceRateFeedID);
    bool enoughReports = (sortedOracles.numRates(exchange.config.referenceRateFeedID) >=
      exchange.config.minimumReports);
    // solhint-disable-next-line not-rely-on-time
    bool medianReportRecent = sortedOracles.medianTimestamp(exchange.config.referenceRateFeedID) >
      now.sub(exchange.config.referenceRateResetFrequency);
    return !isReportExpired && enoughReports && medianReportRecent;
  }

  /**
   * @notice Calculate the new bucket sizes for a exchange.
   * @param exchange The PoolExchange in context.
   * @return bucket0 The size of bucket0.
   * @return bucket1 The size of bucket1.
   */
  function getUpdatedBuckets(PoolExchange memory exchange) internal view returns (uint256 bucket0, uint256 bucket1) {
    bucket0 = exchange.config.stablePoolResetSize;
    uint256 exchangeRateNumerator;
    uint256 exchangeRateDenominator;
    (exchangeRateNumerator, exchangeRateDenominator) = getOracleExchangeRate(exchange.config.referenceRateFeedID);

    bucket1 = exchangeRateDenominator.mul(bucket0).div(exchangeRateNumerator);
  }

  /**
   * @notice Get the exchange rate as numerator,denominator from sorted oracles
   * and protect in case of a 0-denominator.
   * @param target the reportTarget to read from SortedOracles
   * @return rateNumerator
   * @return rateDenominator
   */
  function getOracleExchangeRate(address target)
    internal
    view
    returns (uint256 rateNumerator, uint256 rateDenominator)
  {
    (rateNumerator, rateDenominator) = sortedOracles.medianRate(target);
    require(rateDenominator > 0, "exchange rate denominator must be greater than 0");
  }

  /**
   * @notice Valitates a PoolExchange's parameters and configuration
   * @dev Reverts if not valid
   * @param exchange The PoolExchange to validate
   */
  function validate(PoolExchange memory exchange) private view {
    require(reserve.isStableAsset(exchange.asset0), "asset0 must be a stable registered with the reserve");
    require(
      reserve.isStableAsset(exchange.asset1) || reserve.isCollateralAsset(exchange.asset1),
      "asset1 must be a stable or collateral"
    );
    require(FixidityLib.lte(exchange.config.spread, FixidityLib.fixed1()), "spread must be less than or equal to 1");
    require(exchange.config.referenceRateFeedID != address(0), "referenceRateFeedID must be set");
  }

  /**
   * @notice Get the identifier of the pricing module used by a exchange
   * @param exchange The exchange to get the pricing module identifier for
   * @return The encoded and hashed identifier of the pricing module
   */
  function pricingModuleIdentifier(PoolExchange memory exchange) internal view returns (bytes32) {
    return keccak256(abi.encodePacked(exchange.pricingModule.name()));
  }

  /**
   * @notice Determine whether an exchange is using a constant sum pricing module
   * @param exchange The exchange to check
   * @return bool indicating if the exchange is using a constant sum pricing module
   */
  function isConstantSum(PoolExchange memory exchange) internal view returns (bool) {
    return pricingModuleIdentifier(exchange) == CONSTANT_SUM;
  }

  /**
   * @notice Determine whether an exchange is using a constant product pricing module
   * @param exchange The exchange to check
   * @return bool indicating if the exchange is using a constant product pricing module
   */
  function isConstantProduct(PoolExchange memory exchange) internal view returns (bool) {
    return pricingModuleIdentifier(exchange) == CONSTANT_PRODUCT;
  }
}
        

/lib/mento-core-2.0.0/lib/openzeppelin-contracts/contracts/GSN/Context.sol

pragma solidity ^0.5.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

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

/lib/mento-core-2.0.0/lib/openzeppelin-contracts/contracts/math/SafeMath.sol

pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

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

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

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

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}
          

/lib/mento-core-2.0.0/lib/openzeppelin-contracts/contracts/ownership/Ownable.sol

pragma solidity ^0.5.0;

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

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

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}
          

/lib/mento-core-2.2.0/contracts/common/FixidityLib.sol

pragma solidity ^0.5.13;

/**
 * @title FixidityLib
 * @author Gadi Guy, Alberto Cuesta Canada
 * @notice This library provides fixed point arithmetic with protection against
 * overflow.
 * All operations are done with uint256 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, or with wrap() which expects a number already
 * in the internal representation of a fraction.
 * When using this library be sure to use maxNewFixed() as the upper limit for
 * creation of fixed point numbers.
 * @dev All contained functions are pure and thus marked internal to be inlined
 * on consuming contracts at compile time for gas efficiency.
 */
library FixidityLib {
  struct Fraction {
    uint256 value;
  }

  /**
   * @notice Number of positions that the comma is shifted to the right.
   */
  function digits() internal pure returns (uint8) {
    return 24;
  }

  uint256 private constant FIXED1_UINT = 1000000000000000000000000;

  /**
   * @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() internal pure returns (Fraction memory) {
    return Fraction(FIXED1_UINT);
  }

  /**
   * @notice Wrap a uint256 that represents a 24-decimal fraction in a Fraction
   * struct.
   * @param x Number that already represents a 24-decimal fraction.
   * @return A Fraction struct with contents x.
   */
  function wrap(uint256 x) internal pure returns (Fraction memory) {
    return Fraction(x);
  }

  /**
   * @notice Unwraps the uint256 inside of a Fraction struct.
   */
  function unwrap(Fraction memory x) internal pure returns (uint256) {
    return x.value;
  }

  /**
   * @notice The amount of decimals lost on each multiplication operand.
   * @dev Test mulPrecision() equals sqrt(fixed1)
   */
  function mulPrecision() internal pure returns (uint256) {
    return 1000000000000;
  }

  /**
   * @notice Maximum value that can be converted to fixed point. Optimize for deployment.
   * @dev
   * Test maxNewFixed() equals maxUint256() / fixed1()
   */
  function maxNewFixed() internal pure returns (uint256) {
    return 115792089237316195423570985008687907853269984665640564;
  }

  /**
   * @notice Converts a uint256 to fixed point Fraction
   * @dev Test newFixed(0) returns 0
   * Test newFixed(1) returns fixed1()
   * Test newFixed(maxNewFixed()) returns maxNewFixed() * fixed1()
   * Test newFixed(maxNewFixed()+1) fails
   */
  function newFixed(uint256 x) internal pure returns (Fraction memory) {
    require(x <= maxNewFixed(), "can't create fixidity number larger than maxNewFixed()");
    return Fraction(x * FIXED1_UINT);
  }

  /**
   * @notice Converts a uint256 in the fixed point representation of this
   * library to a non decimal. All decimal digits will be truncated.
   */
  function fromFixed(Fraction memory x) internal pure returns (uint256) {
    return x.value / FIXED1_UINT;
  }

  /**
   * @notice Converts two uint256 representing a fraction to fixed point units,
   * equivalent to multiplying dividend and divisor by 10^digits().
   * @param numerator numerator must be <= maxNewFixed()
   * @param denominator denominator must be <= maxNewFixed() and denominator can't be 0
   * @dev
   * Test newFixedFraction(1,0) fails
   * Test newFixedFraction(0,1) returns 0
   * Test newFixedFraction(1,1) returns fixed1()
   * Test newFixedFraction(1,fixed1()) returns 1
   */
  function newFixedFraction(uint256 numerator, uint256 denominator) internal pure returns (Fraction memory) {
    Fraction memory convertedNumerator = newFixed(numerator);
    Fraction memory 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()
   */
  function integer(Fraction memory x) internal pure returns (Fraction memory) {
    return Fraction((x.value / FIXED1_UINT) * FIXED1_UINT); // 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
   */
  function fractional(Fraction memory x) internal pure returns (Fraction memory) {
    return Fraction(x.value - (x.value / FIXED1_UINT) * FIXED1_UINT); // Can't overflow
  }

  /**
   * @notice x+y.
   * @dev The maximum value that can be safely used as an addition operator is defined as
   * maxFixedAdd = maxUint256()-1 / 2, or
   * 57896044618658097711785492504343953926634992332820282019728792003956564819967.
   * Test add(maxFixedAdd,maxFixedAdd) equals maxFixedAdd + maxFixedAdd
   * Test add(maxFixedAdd+1,maxFixedAdd+1) throws
   */
  function add(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    uint256 z = x.value + y.value;
    require(z >= x.value, "add overflow detected");
    return Fraction(z);
  }

  /**
   * @notice x-y.
   * @dev
   * Test subtract(6, 10) fails
   */
  function subtract(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    require(x.value >= y.value, "substraction underflow detected");
    return Fraction(x.value - y.value);
  }

  /**
   * @notice x*y. If any of the operators is higher than the max multiplier value it
   * might overflow.
   * @dev The maximum value that can be safely used as a multiplication operator
   * (maxFixedMul) is calculated as sqrt(maxUint256()*fixed1()),
   * or 340282366920938463463374607431768211455999999999999
   * Test multiply(0,0) returns 0
   * Test multiply(maxFixedMul,0) returns 0
   * Test multiply(0,maxFixedMul) returns 0
   * Test multiply(fixed1()/mulPrecision(),fixed1()*mulPrecision()) returns fixed1()
   * Test multiply(maxFixedMul,maxFixedMul) is around maxUint256()
   * Test multiply(maxFixedMul+1,maxFixedMul+1) fails
   */
  // solhint-disable-next-line code-complexity
  function multiply(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    if (x.value == 0 || y.value == 0) return Fraction(0);
    if (y.value == FIXED1_UINT) return x;
    if (x.value == FIXED1_UINT) return y;

    // Separate into integer and fractional parts
    // x = x1 + x2, y = y1 + y2
    uint256 x1 = integer(x).value / FIXED1_UINT;
    uint256 x2 = fractional(x).value;
    uint256 y1 = integer(y).value / FIXED1_UINT;
    uint256 y2 = fractional(y).value;

    // (x1 + x2) * (y1 + y2) = (x1 * y1) + (x1 * y2) + (x2 * y1) + (x2 * y2)
    uint256 x1y1 = x1 * y1;
    if (x1 != 0) require(x1y1 / x1 == y1, "overflow x1y1 detected");

    // x1y1 needs to be multiplied back by fixed1
    // solhint-disable-next-line var-name-mixedcase
    uint256 fixed_x1y1 = x1y1 * FIXED1_UINT;
    if (x1y1 != 0) require(fixed_x1y1 / x1y1 == FIXED1_UINT, "overflow x1y1 * fixed1 detected");
    x1y1 = fixed_x1y1;

    uint256 x2y1 = x2 * y1;
    if (x2 != 0) require(x2y1 / x2 == y1, "overflow x2y1 detected");

    uint256 x1y2 = x1 * y2;
    if (x1 != 0) require(x1y2 / x1 == y2, "overflow x1y2 detected");

    x2 = x2 / mulPrecision();
    y2 = y2 / mulPrecision();
    uint256 x2y2 = x2 * y2;
    if (x2 != 0) require(x2y2 / x2 == y2, "overflow x2y2 detected");

    // result = fixed1() * x1 * y1 + x1 * y2 + x2 * y1 + x2 * y2 / fixed1();
    Fraction memory result = Fraction(x1y1);
    result = add(result, Fraction(x2y1)); // Add checks for overflow
    result = add(result, Fraction(x1y2)); // Add checks for overflow
    result = add(result, Fraction(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(1+fixed1()*fixed1()) returns 0 // Testing how the fractional is truncated
   * Test reciprocal(newFixedFraction(1, 1e24)) returns newFixed(1e24)
   */
  function reciprocal(Fraction memory x) internal pure returns (Fraction memory) {
    require(x.value != 0, "can't call reciprocal(0)");
    return Fraction((FIXED1_UINT * FIXED1_UINT) / x.value); // Can't overflow
  }

  /**
   * @notice x/y. If the dividend is higher than the max dividend value, it
   * might overflow. You can use multiply(x,reciprocal(y)) instead.
   * @dev The maximum value that can be safely used as a dividend (maxNewFixed) is defined as
   * divide(maxNewFixed,newFixedFraction(1,fixed1())) is around maxUint256().
   * This yields the value 115792089237316195423570985008687907853269984665640564.
   * Test maxNewFixed equals maxUint256()/fixed1()
   * Test divide(maxNewFixed,1) equals maxNewFixed*(fixed1)
   * Test divide(maxNewFixed+1,multiply(mulPrecision(),mulPrecision())) throws
   * Test divide(fixed1(),0) fails
   * Test divide(maxNewFixed,1) = maxNewFixed*(10^digits())
   * Test divide(maxNewFixed+1,1) throws
   */
  function divide(Fraction memory x, Fraction memory y) internal pure returns (Fraction memory) {
    require(y.value != 0, "can't divide by 0");
    // solhint-disable-next-line var-name-mixedcase
    uint256 X = x.value * FIXED1_UINT;
    require(X / FIXED1_UINT == x.value, "overflow at divide");
    return Fraction(X / y.value);
  }

  /**
   * @notice x > y
   */
  function gt(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value > y.value;
  }

  /**
   * @notice x >= y
   */
  function gte(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value >= y.value;
  }

  /**
   * @notice x < y
   */
  function lt(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value < y.value;
  }

  /**
   * @notice x <= y
   */
  function lte(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value <= y.value;
  }

  /**
   * @notice x == y
   */
  function equals(Fraction memory x, Fraction memory y) internal pure returns (bool) {
    return x.value == y.value;
  }

  /**
   * @notice x <= 1
   */
  function isProperFraction(Fraction memory x) internal pure returns (bool) {
    return lte(x, fixed1());
  }
}
          

/lib/mento-core-2.2.0/contracts/common/Initializable.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

contract Initializable {
  bool public initialized;

  constructor(bool testingDeployment) public {
    if (!testingDeployment) {
      initialized = true;
    }
  }

  modifier initializer() {
    require(!initialized, "contract already initialized");
    initialized = true;
    _;
  }
}
          

/lib/mento-core-2.2.0/contracts/common/interfaces/IERC20Metadata.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

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

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

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

  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

/lib/mento-core-2.2.0/contracts/common/linkedlists/LinkedList.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
 * @title Maintains a doubly linked list keyed by bytes32.
 * @dev Following the `next` pointers will lead you to the head, rather than the tail.
 */
library LinkedList {
  using SafeMath for uint256;

  struct Element {
    bytes32 previousKey;
    bytes32 nextKey;
    bool exists;
  }

  struct List {
    bytes32 head;
    bytes32 tail;
    uint256 numElements;
    mapping(bytes32 => Element) elements;
  }

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param previousKey The key of the element that comes before the element to insert.
   * @param nextKey The key of the element that comes after the element to insert.
   */
  function insert(
    List storage list,
    bytes32 key,
    bytes32 previousKey,
    bytes32 nextKey
  ) internal {
    require(key != bytes32(0), "Key must be defined");
    require(!contains(list, key), "Can't insert an existing element");
    require(previousKey != key && nextKey != key, "Key cannot be the same as previousKey or nextKey");

    Element storage element = list.elements[key];
    element.exists = true;

    if (list.numElements == 0) {
      list.tail = key;
      list.head = key;
    } else {
      require(previousKey != bytes32(0) || nextKey != bytes32(0), "Either previousKey or nextKey must be defined");

      element.previousKey = previousKey;
      element.nextKey = nextKey;

      if (previousKey != bytes32(0)) {
        require(contains(list, previousKey), "If previousKey is defined, it must exist in the list");
        Element storage previousElement = list.elements[previousKey];
        require(previousElement.nextKey == nextKey, "previousKey must be adjacent to nextKey");
        previousElement.nextKey = key;
      } else {
        list.tail = key;
      }

      if (nextKey != bytes32(0)) {
        require(contains(list, nextKey), "If nextKey is defined, it must exist in the list");
        Element storage nextElement = list.elements[nextKey];
        require(nextElement.previousKey == previousKey, "previousKey must be adjacent to nextKey");
        nextElement.previousKey = key;
      } else {
        list.head = key;
      }
    }

    list.numElements = list.numElements.add(1);
  }

  /**
   * @notice Inserts an element at the tail of the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   */
  function push(List storage list, bytes32 key) internal {
    insert(list, key, bytes32(0), list.tail);
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(List storage list, bytes32 key) internal {
    Element storage element = list.elements[key];
    require(key != bytes32(0) && contains(list, key), "key not in list");
    if (element.previousKey != bytes32(0)) {
      Element storage previousElement = list.elements[element.previousKey];
      previousElement.nextKey = element.nextKey;
    } else {
      list.tail = element.nextKey;
    }

    if (element.nextKey != bytes32(0)) {
      Element storage nextElement = list.elements[element.nextKey];
      nextElement.previousKey = element.previousKey;
    } else {
      list.head = element.previousKey;
    }

    delete list.elements[key];
    list.numElements = list.numElements.sub(1);
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param previousKey The key of the element that comes before the updated element.
   * @param nextKey The key of the element that comes after the updated element.
   */
  function update(
    List storage list,
    bytes32 key,
    bytes32 previousKey,
    bytes32 nextKey
  ) internal {
    require(key != bytes32(0) && key != previousKey && key != nextKey && contains(list, key), "key on in list");
    remove(list, key);
    insert(list, key, previousKey, nextKey);
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(List storage list, bytes32 key) internal view returns (bool) {
    return list.elements[key].exists;
  }

  /**
   * @notice Returns the keys of the N elements at the head of the list.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to return.
   * @return The keys of the N elements at the head of the list.
   * @dev Reverts if n is greater than the number of elements in the list.
   */
  function headN(List storage list, uint256 n) internal view returns (bytes32[] memory) {
    require(n <= list.numElements, "not enough elements");
    bytes32[] memory keys = new bytes32[](n);
    bytes32 key = list.head;
    for (uint256 i = 0; i < n; i = i.add(1)) {
      keys[i] = key;
      key = list.elements[key].previousKey;
    }
    return keys;
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(List storage list) internal view returns (bytes32[] memory) {
    return headN(list, list.numElements);
  }
}
          

/lib/mento-core-2.2.0/contracts/common/linkedlists/SortedLinkedList.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./LinkedList.sol";

/**
 * @title Maintains a sorted list of unsigned ints keyed by bytes32.
 */
library SortedLinkedList {
  using SafeMath for uint256;
  using LinkedList for LinkedList.List;

  struct List {
    LinkedList.List list;
    mapping(bytes32 => uint256) values;
  }

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param value The element value.
   * @param lesserKey The key of the element less than the element to insert.
   * @param greaterKey The key of the element greater than the element to insert.
   */
  function insert(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    require(key != bytes32(0) && key != lesserKey && key != greaterKey && !contains(list, key), "invalid key");
    require(
      (lesserKey != bytes32(0) || greaterKey != bytes32(0)) || list.list.numElements == 0,
      "greater and lesser key zero"
    );
    require(contains(list, lesserKey) || lesserKey == bytes32(0), "invalid lesser key");
    require(contains(list, greaterKey) || greaterKey == bytes32(0), "invalid greater key");
    (lesserKey, greaterKey) = getLesserAndGreater(list, value, lesserKey, greaterKey);
    list.list.insert(key, lesserKey, greaterKey);
    list.values[key] = value;
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(List storage list, bytes32 key) internal {
    list.list.remove(key);
    list.values[key] = 0;
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param value The element value.
   * @param lesserKey The key of the element will be just left of `key` after the update.
   * @param greaterKey The key of the element will be just right of `key` after the update.
   * @dev Note that only one of "lesserKey" or "greaterKey" needs to be correct to reduce friction.
   */
  function update(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    remove(list, key);
    insert(list, key, value, lesserKey, greaterKey);
  }

  /**
   * @notice Inserts an element at the tail of the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   */
  function push(List storage list, bytes32 key) internal {
    insert(list, key, 0, bytes32(0), list.list.tail);
  }

  /**
   * @notice Removes N elements from the head of the list and returns their keys.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to pop.
   * @return The keys of the popped elements.
   */
  function popN(List storage list, uint256 n) internal returns (bytes32[] memory) {
    require(n <= list.list.numElements, "not enough elements");
    bytes32[] memory keys = new bytes32[](n);
    for (uint256 i = 0; i < n; i = i.add(1)) {
      bytes32 key = list.list.head;
      keys[i] = key;
      remove(list, key);
    }
    return keys;
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(List storage list, bytes32 key) internal view returns (bool) {
    return list.list.contains(key);
  }

  /**
   * @notice Returns the value for a particular key in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return The element value.
   */
  function getValue(List storage list, bytes32 key) internal view returns (uint256) {
    return list.values[key];
  }

  /**
   * @notice Gets all elements from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return Array of all keys in the list.
   * @return Values corresponding to keys, which will be ordered largest to smallest.
   */
  function getElements(List storage list) internal view returns (bytes32[] memory, uint256[] memory) {
    bytes32[] memory keys = getKeys(list);
    uint256[] memory values = new uint256[](keys.length);
    for (uint256 i = 0; i < keys.length; i = i.add(1)) {
      values[i] = list.values[keys[i]];
    }
    return (keys, values);
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(List storage list) internal view returns (bytes32[] memory) {
    return list.list.getKeys();
  }

  /**
   * @notice Returns first N greatest elements of the list.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to return.
   * @return The keys of the first n elements.
   * @dev Reverts if n is greater than the number of elements in the list.
   */
  function headN(List storage list, uint256 n) internal view returns (bytes32[] memory) {
    return list.list.headN(n);
  }

  /**
   * @notice Returns the keys of the elements greaterKey than and less than the provided value.
   * @param list A storage pointer to the underlying list.
   * @param value The element value.
   * @param lesserKey The key of the element which could be just left of the new value.
   * @param greaterKey The key of the element which could be just right of the new value.
   * @return The correct lesserKey keys.
   * @return The correct greaterKey keys.
   */
  function getLesserAndGreater(
    List storage list,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) private view returns (bytes32, bytes32) {
    // Check for one of the following conditions and fail if none are met:
    //   1. The value is less than the current lowest value
    //   2. The value is greater than the current greatest value
    //   3. The value is just greater than the value for `lesserKey`
    //   4. The value is just less than the value for `greaterKey`
    if (lesserKey == bytes32(0) && isValueBetween(list, value, lesserKey, list.list.tail)) {
      return (lesserKey, list.list.tail);
    } else if (greaterKey == bytes32(0) && isValueBetween(list, value, list.list.head, greaterKey)) {
      return (list.list.head, greaterKey);
    } else if (
      lesserKey != bytes32(0) && isValueBetween(list, value, lesserKey, list.list.elements[lesserKey].nextKey)
    ) {
      return (lesserKey, list.list.elements[lesserKey].nextKey);
    } else if (
      greaterKey != bytes32(0) && isValueBetween(list, value, list.list.elements[greaterKey].previousKey, greaterKey)
    ) {
      return (list.list.elements[greaterKey].previousKey, greaterKey);
    } else {
      require(false, "get lesser and greater failure");
    }
  }

  /**
   * @notice Returns whether or not a given element is between two other elements.
   * @param list A storage pointer to the underlying list.
   * @param value The element value.
   * @param lesserKey The key of the element whose value should be lesserKey.
   * @param greaterKey The key of the element whose value should be greaterKey.
   * @return True if the given element is between the two other elements.
   */
  function isValueBetween(
    List storage list,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) private view returns (bool) {
    bool isLesser = lesserKey == bytes32(0) || list.values[lesserKey] <= value;
    bool isGreater = greaterKey == bytes32(0) || list.values[greaterKey] >= value;
    return isLesser && isGreater;
  }
}
          

/lib/mento-core-2.2.0/contracts/common/linkedlists/SortedLinkedListWithMedian.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./LinkedList.sol";
import "./SortedLinkedList.sol";

/**
 * @title Maintains a sorted list of unsigned ints keyed by bytes32.
 */
library SortedLinkedListWithMedian {
  using SafeMath for uint256;
  using SortedLinkedList for SortedLinkedList.List;

  enum MedianAction {
    None,
    Lesser,
    Greater
  }

  enum MedianRelation {
    Undefined,
    Lesser,
    Greater,
    Equal
  }

  struct List {
    SortedLinkedList.List list;
    bytes32 median;
    mapping(bytes32 => MedianRelation) relation;
  }

  /**
   * @notice Inserts an element into a doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   * @param value The element value.
   * @param lesserKey The key of the element less than the element to insert.
   * @param greaterKey The key of the element greater than the element to insert.
   */
  function insert(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    list.list.insert(key, value, lesserKey, greaterKey);
    LinkedList.Element storage element = list.list.list.elements[key];

    MedianAction action = MedianAction.None;
    if (list.list.list.numElements == 1) {
      list.median = key;
      list.relation[key] = MedianRelation.Equal;
    } else if (list.list.list.numElements % 2 == 1) {
      // When we have an odd number of elements, and the element that we inserted is less than
      // the previous median, we need to slide the median down one element, since we had previously
      // selected the greater of the two middle elements.
      if (element.previousKey == bytes32(0) || list.relation[element.previousKey] == MedianRelation.Lesser) {
        action = MedianAction.Lesser;
        list.relation[key] = MedianRelation.Lesser;
      } else {
        list.relation[key] = MedianRelation.Greater;
      }
    } else {
      // When we have an even number of elements, and the element that we inserted is greater than
      // the previous median, we need to slide the median up one element, since we always select
      // the greater of the two middle elements.
      if (element.nextKey == bytes32(0) || list.relation[element.nextKey] == MedianRelation.Greater) {
        action = MedianAction.Greater;
        list.relation[key] = MedianRelation.Greater;
      } else {
        list.relation[key] = MedianRelation.Lesser;
      }
    }
    updateMedian(list, action);
  }

  /**
   * @notice Removes an element from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to remove.
   */
  function remove(List storage list, bytes32 key) internal {
    MedianAction action = MedianAction.None;
    if (list.list.list.numElements == 0) {
      list.median = bytes32(0);
    } else if (list.list.list.numElements % 2 == 0) {
      // When we have an even number of elements, we always choose the higher of the two medians.
      // Thus, if the element we're removing is greaterKey than or equal to the median we need to
      // slide the median left by one.
      if (list.relation[key] == MedianRelation.Greater || list.relation[key] == MedianRelation.Equal) {
        action = MedianAction.Lesser;
      }
    } else {
      // When we don't have an even number of elements, we just choose the median value.
      // Thus, if the element we're removing is less than or equal to the median, we need to slide
      // median right by one.
      if (list.relation[key] == MedianRelation.Lesser || list.relation[key] == MedianRelation.Equal) {
        action = MedianAction.Greater;
      }
    }
    updateMedian(list, action);

    list.list.remove(key);
  }

  /**
   * @notice Updates an element in the list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @param value The element value.
   * @param lesserKey The key of the element will be just left of `key` after the update.
   * @param greaterKey The key of the element will be just right of `key` after the update.
   * @dev Note that only one of "lesserKey" or "greaterKey" needs to be correct to reduce friction.
   */
  function update(
    List storage list,
    bytes32 key,
    uint256 value,
    bytes32 lesserKey,
    bytes32 greaterKey
  ) internal {
    remove(list, key);
    insert(list, key, value, lesserKey, greaterKey);
  }

  /**
   * @notice Inserts an element at the tail of the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @param key The key of the element to insert.
   */
  function push(List storage list, bytes32 key) internal {
    insert(list, key, 0, bytes32(0), list.list.list.tail);
  }

  /**
   * @notice Removes N elements from the head of the list and returns their keys.
   * @param list A storage pointer to the underlying list.
   * @param n The number of elements to pop.
   * @return The keys of the popped elements.
   */
  function popN(List storage list, uint256 n) internal returns (bytes32[] memory) {
    require(n <= list.list.list.numElements, "not enough elements");
    bytes32[] memory keys = new bytes32[](n);
    for (uint256 i = 0; i < n; i = i.add(1)) {
      bytes32 key = list.list.list.head;
      keys[i] = key;
      remove(list, key);
    }
    return keys;
  }

  /**
   * @notice Returns whether or not a particular key is present in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return Whether or not the key is in the sorted list.
   */
  function contains(List storage list, bytes32 key) internal view returns (bool) {
    return list.list.contains(key);
  }

  /**
   * @notice Returns the value for a particular key in the sorted list.
   * @param list A storage pointer to the underlying list.
   * @param key The element key.
   * @return The element value.
   */
  function getValue(List storage list, bytes32 key) internal view returns (uint256) {
    return list.list.values[key];
  }

  /**
   * @notice Returns the median value of the sorted list.
   * @param list A storage pointer to the underlying list.
   * @return The median value.
   */
  function getMedianValue(List storage list) internal view returns (uint256) {
    return getValue(list, list.median);
  }

  /**
   * @notice Returns the key of the first element in the list.
   * @param list A storage pointer to the underlying list.
   * @return The key of the first element in the list.
   */
  function getHead(List storage list) internal view returns (bytes32) {
    return list.list.list.head;
  }

  /**
   * @notice Returns the key of the median element in the list.
   * @param list A storage pointer to the underlying list.
   * @return The key of the median element in the list.
   */
  function getMedian(List storage list) internal view returns (bytes32) {
    return list.median;
  }

  /**
   * @notice Returns the key of the last element in the list.
   * @param list A storage pointer to the underlying list.
   * @return The key of the last element in the list.
   */
  function getTail(List storage list) internal view returns (bytes32) {
    return list.list.list.tail;
  }

  /**
   * @notice Returns the number of elements in the list.
   * @param list A storage pointer to the underlying list.
   * @return The number of elements in the list.
   */
  function getNumElements(List storage list) internal view returns (uint256) {
    return list.list.list.numElements;
  }

  /**
   * @notice Gets all elements from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return Array of all keys in the list.
   * @return Values corresponding to keys, which will be ordered largest to smallest.
   * @return Array of relations to median of corresponding list elements.
   */
  function getElements(List storage list)
    internal
    view
    returns (
      bytes32[] memory,
      uint256[] memory,
      MedianRelation[] memory
    )
  {
    bytes32[] memory keys = getKeys(list);
    uint256[] memory values = new uint256[](keys.length);
    MedianRelation[] memory relations = new MedianRelation[](keys.length);
    for (uint256 i = 0; i < keys.length; i = i.add(1)) {
      values[i] = list.list.values[keys[i]];
      relations[i] = list.relation[keys[i]];
    }
    return (keys, values, relations);
  }

  /**
   * @notice Gets all element keys from the doubly linked list.
   * @param list A storage pointer to the underlying list.
   * @return All element keys from head to tail.
   */
  function getKeys(List storage list) internal view returns (bytes32[] memory) {
    return list.list.getKeys();
  }

  /**
   * @notice Moves the median pointer right or left of its current value.
   * @param list A storage pointer to the underlying list.
   * @param action Which direction to move the median pointer.
   */
  function updateMedian(List storage list, MedianAction action) private {
    LinkedList.Element storage previousMedian = list.list.list.elements[list.median];
    if (action == MedianAction.Lesser) {
      list.relation[list.median] = MedianRelation.Greater;
      list.median = previousMedian.previousKey;
    } else if (action == MedianAction.Greater) {
      list.relation[list.median] = MedianRelation.Lesser;
      list.median = previousMedian.nextKey;
    }
    list.relation[list.median] = MedianRelation.Equal;
  }
}
          

/lib/mento-core-2.2.0/contracts/interfaces/IBiPoolManager.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

import { IPricingModule } from "./IPricingModule.sol";
import { FixidityLib } from "../common/FixidityLib.sol";

/**
 * @title BiPool Manager interface
 * @notice The two asset pool manager is responsible for
 * managing the state of all two-asset virtual pools.
 */
interface IBiPoolManager {
  /**
   * @title PoolExchange
   * @notice The PoolExchange is a type of asset exchange that
   * that implements an AMM with two virtual buckets.
   */
  struct PoolExchange {
    address asset0;
    address asset1;
    IPricingModule pricingModule;
    uint256 bucket0;
    uint256 bucket1;
    uint256 lastBucketUpdate;
    PoolConfig config;
  }

  /**
   * @notice Variables related to bucket updates and sizing.
   * @dev Broken down into a separate struct because the compiler
   * version doesn't support structs with too many members.
   * Sad reacts only.
   */
  struct PoolConfig {
    FixidityLib.Fraction spread;
    address referenceRateFeedID; // rateFeedID of the price that this pool follows (i.e. it's reference rate)
    uint256 referenceRateResetFrequency;
    uint256 minimumReports;
    uint256 stablePoolResetSize;
  }

  /**
   * @notice Emitted when a new PoolExchange has been created.
   * @param exchangeId The id of the new PoolExchange
   * @param asset0 The address of asset0
   * @param asset1 The address of asset1
   * @param pricingModule the address of the pricingModule
   */
  event ExchangeCreated(
    bytes32 indexed exchangeId,
    address indexed asset0,
    address indexed asset1,
    address pricingModule
  );

  /**
   * @notice Emitted when a PoolExchange has been destroyed.
   * @param exchangeId The id of the PoolExchange
   * @param asset0 The address of asset0
   * @param asset1 The address of asset1
   * @param pricingModule the address of the pricingModule
   */
  event ExchangeDestroyed(
    bytes32 indexed exchangeId,
    address indexed asset0,
    address indexed asset1,
    address pricingModule
  );

  /**
   * @notice Emitted when the broker address is updated.
   * @param newBroker The address of the new broker.
   */
  event BrokerUpdated(address indexed newBroker);

  /**
   * @notice Emitted when the reserve address is updated.
   * @param newReserve The address of the new reserve.
   */
  event ReserveUpdated(address indexed newReserve);

  /**
   * @notice Emitted when the breakerBox address is updated.
   * @param newBreakerBox The address of the new breakerBox.
   */
  event BreakerBoxUpdated(address newBreakerBox);

  /**
   * @notice Emitted when the sortedOracles address is updated.
   * @param newSortedOracles The address of the new sortedOracles.
   */
  event SortedOraclesUpdated(address indexed newSortedOracles);

  /**
   * @notice Emitted when the buckets for a specified exchange are updated.
   * @param exchangeId The id of the exchange
   * @param bucket0 The new bucket0 size
   * @param bucket1 The new bucket1 size
   */
  event BucketsUpdated(bytes32 indexed exchangeId, uint256 bucket0, uint256 bucket1);

  /**
   * @notice Emitted when the pricing modules have been updated.
   * @param newIdentifiers The new identifiers.
   * @param newAddresses The new pricing module addresses.
   */
  event PricingModulesUpdated(bytes32[] newIdentifiers, address[] newAddresses);

  /**
   * @notice Retrieves the pool with the specified exchangeId.
   * @param exchangeId The id of the pool to be retrieved.
   * @return exchange The PoolExchange with that ID.
   */
  function getPoolExchange(bytes32 exchangeId) external view returns (PoolExchange memory exchange);

  /**
   * @notice Get all exchange IDs.
   * @return exchangeIds List of the exchangeIds.
   */
  function getExchangeIds() external view returns (bytes32[] memory exchangeIds);

  /**
   * @notice Create a PoolExchange with the provided data.
   * @param exchange The PoolExchange to be created.
   * @return exchangeId The id of the exchange.
   */
  function createExchange(PoolExchange calldata exchange) external returns (bytes32 exchangeId);

  /**
   * @notice Delete a PoolExchange.
   * @param exchangeId The PoolExchange to be created.
   * @param exchangeIdIndex The index of the exchangeId in the exchangeIds array.
   * @return destroyed - true on successful delition.
   */
  function destroyExchange(bytes32 exchangeId, uint256 exchangeIdIndex) external returns (bool destroyed);
}
          

/lib/mento-core-2.2.0/contracts/interfaces/IBreakerBox.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

/**
 * @title Breaker Box Interface
 * @notice Defines the basic interface for the Breaker Box
 */
interface IBreakerBox {
  /**
   * @dev Used to keep track of the status of a breaker for a specific rate feed.
   *
   * - TradingMode: Represents the trading mode the breaker is in for a rate feed.
   *                This uses a bitmask approach, meaning each bit represents a
   *                different trading mode. The final trading mode of the rate feed
   *                is obtained by applying a logical OR operation to the TradingMode
   *                of all breakers associated with that rate feed. This allows multiple
   *                breakers to contribute to the final trading mode simultaneously.
   *                Possible values:
   *                0: bidirectional trading.
   *                1: inflow only.
   *                2: outflow only.
   *                3: trading halted.
   *
   * - LastUpdatedTime: Records the last time the breaker status was updated. This is
   *                    used to manage cooldown periods before the breaker can be reset.
   *
   * - Enabled:     Indicates whether the breaker is enabled for the associated rate feed.
   */
  struct BreakerStatus {
    uint8 tradingMode;
    uint64 lastUpdatedTime;
    bool enabled;
  }

  /**
   * @notice Emitted when a new breaker is added to the breaker box.
   * @param breaker The address of the breaker.
   */
  event BreakerAdded(address indexed breaker);

  /**
   * @notice Emitted when a breaker is removed from the breaker box.
   * @param breaker The address of the breaker.
   */
  event BreakerRemoved(address indexed breaker);

  /**
   * @notice Emitted when a breaker is tripped by a rate feed.
   * @param breaker The address of the breaker.
   * @param rateFeedID The address of the rate feed.
   */
  event BreakerTripped(address indexed breaker, address indexed rateFeedID);

  /**
   * @notice Emitted when a new rate feed is added to the breaker box.
   * @param rateFeedID The address of the rate feed.
   */
  event RateFeedAdded(address indexed rateFeedID);

  /**
   * @notice Emitted when dependencies for a rate feed are set.
   * @param rateFeedID The address of the rate feed.
   * @param dependencies The addresses of the dependendent rate feeds.
   */
  event RateFeedDependenciesSet(address indexed rateFeedID, address[] indexed dependencies);

  /**
   * @notice Emitted when a rate feed is removed from the breaker box.
   * @param rateFeedID The address of the rate feed.
   */
  event RateFeedRemoved(address indexed rateFeedID);

  /**
   * @notice Emitted when the trading mode for a rate feed is updated
   * @param rateFeedID The address of the rate feed.
   * @param tradingMode The new trading mode.
   */
  event TradingModeUpdated(address indexed rateFeedID, uint256 tradingMode);

  /**
   * @notice Emitted after a reset attempt is successful.
   * @param rateFeedID The address of the rate feed.
   * @param breaker The address of the breaker.
   */
  event ResetSuccessful(address indexed rateFeedID, address indexed breaker);

  /**
   * @notice  Emitted after a reset attempt fails when the
   *          rate feed fails the breakers reset criteria.
   * @param rateFeedID The address of the rate feed.
   * @param breaker The address of the breaker.
   */
  event ResetAttemptCriteriaFail(address indexed rateFeedID, address indexed breaker);

  /**
   * @notice Emitted after a reset attempt fails when cooldown time has not elapsed.
   * @param rateFeedID The address of the rate feed.
   * @param breaker The address of the breaker.
   */
  event ResetAttemptNotCool(address indexed rateFeedID, address indexed breaker);

  /**
   * @notice Emitted when the sortedOracles address is updated.
   * @param newSortedOracles The address of the new sortedOracles.
   */
  event SortedOraclesUpdated(address indexed newSortedOracles);

  /**
   * @notice Emitted when the breaker is enabled or disabled for a rate feed.
   * @param breaker The address of the breaker.
   * @param rateFeedID The address of the rate feed.
   * @param status Indicating the status.
   */
  event BreakerStatusUpdated(address breaker, address rateFeedID, bool status);

  /**
   * @notice Retrives an array of all breaker addresses.
   */
  function getBreakers() external view returns (address[] memory);

  /**
   * @notice Checks if a breaker with the specified address has been added to the breaker box.
   * @param breaker The address of the breaker to check;
   * @return A bool indicating whether or not the breaker has been added.
   */
  function isBreaker(address breaker) external view returns (bool);

  /**
   * @notice Checks breakers for the rateFeedID and sets correct trading mode
   * if any breakers are tripped or need to be reset.
   * @param rateFeedID The address of the rate feed to run checks for.
   */
  function checkAndSetBreakers(address rateFeedID) external;

  /**
   * @notice Gets the trading mode for the specified rateFeedID.
   * @param rateFeedID The address of the rate feed to retrieve the trading mode for.
   */
  function getRateFeedTradingMode(address rateFeedID) external view returns (uint8 tradingMode);
}
          

/lib/mento-core-2.2.0/contracts/interfaces/IExchangeProvider.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

/**
 * @title ExchangeProvider interface
 * @notice The IExchangeProvider interface is the interface that the Broker uses
 * to communicate with different exchange manager implementations like the BiPoolManager
 */
interface IExchangeProvider {
  /**
   * @notice Exchange - a struct that's used only by UIs (frontends/CLIs)
   * in order to discover what asset swaps are possible within an
   * exchange provider.
   * It's up to the specific exchange provider to convert its internal
   * representation to this universal struct. This conversion should
   * only happen in view calls used for discovery.
   * @param exchangeId The ID of the exchange, used to initiate swaps or get quotes.
   * @param assets An array of addresses of ERC20 tokens that can be swapped.
   */
  struct Exchange {
    bytes32 exchangeId;
    address[] assets;
  }

  /**
   * @notice Get all exchanges supported by the ExchangeProvider.
   * @return exchanges An array of Exchange structs.
   */
  function getExchanges() external view returns (Exchange[] memory exchanges);

  /**
   * @notice Execute a token swap with fixed amountIn
   * @param exchangeId The id of the exchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountIn The amount of tokenIn to be sold
   * @return amountOut The amount of tokenOut to be bought
   */
  function swapIn(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external returns (uint256 amountOut);

  /**
   * @notice Execute a token swap with fixed amountOut
   * @param exchangeId The id of the exchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountOut The amount of tokenOut to be bought
   * @return amountIn The amount of tokenIn to be sold
   */
  function swapOut(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external returns (uint256 amountIn);

  /**
   * @notice Calculate amountOut of tokenOut received for a given amountIn of tokenIn
   * @param exchangeId The id of the exchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountIn The amount of tokenIn to be sold
   * @return amountOut The amount of tokenOut to be bought
   */
  function getAmountOut(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external view returns (uint256 amountOut);

  /**
   * @notice Calculate amountIn of tokenIn needed for a given amountOut of tokenOut
   * @param exchangeId The id of the exchange to use
   * @param tokenIn The token to be sold
   * @param tokenOut The token to be bought
   * @param amountOut The amount of tokenOut to be bought
   * @return amountIn The amount of tokenIn to be sold
   */
  function getAmountIn(
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external view returns (uint256 amountIn);
}
          

/lib/mento-core-2.2.0/contracts/interfaces/IPricingModule.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

/**
 * @title Interface for a Mento Pricing Module.
 * @notice A Mento pricing module represents an exchange relation between a pair of ERC20 assets.
 */
interface IPricingModule {
  /**
   * @notice Returns the output amount and new bucket sizes for a given input amount.
   * @param tokenInBucketSize Size of the tokenIn bucket.
   * @param tokenOutBucketSize Size of the tokenOut bucket.
   * @param spread Spread charged on exchanges.
   * @param amountIn Amount of tokenIn being paid in.
   * @return amountOut Amount of tokenOut that will be paid out.
   */
  function getAmountOut(
    uint256 tokenInBucketSize,
    uint256 tokenOutBucketSize,
    uint256 spread,
    uint256 amountIn
  ) external view returns (uint256 amountOut);

  /**
   * @notice Returns the input amount necessary for a given output amount.
   * @param tokenInBucketSize Size of the tokenIn bucket.
   * @param tokenOutBucketSize Size of the tokenOut bucket.
   * @param spread Spread charged on exchanges.
   * @param amountOut Amount of tokenIn being paid out.
   * @return amountIn Amount of tokenOut that would have to be paid in.
   */
  function getAmountIn(
    uint256 tokenInBucketSize,
    uint256 tokenOutBucketSize,
    uint256 spread,
    uint256 amountOut
  ) external view returns (uint256 amountIn);

  /**
   * @notice Retrieve the name of this pricing module.
   * @return exchangeName The name of the pricing module.
   */
  function name() external view returns (string memory pricingModuleName);
}
          

/lib/mento-core-2.2.0/contracts/interfaces/IReserve.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

interface IReserve {
  function setTobinTaxStalenessThreshold(uint256) external;

  function addToken(address) external returns (bool);

  function removeToken(address, uint256) external returns (bool);

  function transferGold(address payable, uint256) external returns (bool);

  function transferExchangeGold(address payable, uint256) external returns (bool);

  function transferCollateralAsset(
    address collateralAsset,
    address payable to,
    uint256 value
  ) external returns (bool);

  function getReserveGoldBalance() external view returns (uint256);

  function getUnfrozenReserveGoldBalance() external view returns (uint256);

  function getOrComputeTobinTax() external returns (uint256, uint256);

  function getTokens() external view returns (address[] memory);

  function getReserveRatio() external view returns (uint256);

  function addExchangeSpender(address) external;

  function removeExchangeSpender(address, uint256) external;

  function addSpender(address) external;

  function removeSpender(address) external;

  function isStableAsset(address) external view returns (bool);

  function isCollateralAsset(address) external view returns (bool);

  function getDailySpendingRatioForCollateralAsset(address collateralAsset) external view returns (uint256);

  function isExchangeSpender(address exchange) external view returns (bool);

  function addCollateralAsset(address asset) external returns (bool);

  function transferExchangeCollateralAsset(
    address collateralAsset,
    address payable to,
    uint256 value
  ) external returns (bool);
}
          

/lib/mento-core-2.2.0/contracts/interfaces/ISortedOracles.sol

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.5.13;

import "../common/linkedlists/SortedLinkedListWithMedian.sol";

interface ISortedOracles {
  function addOracle(address, address) external;

  function removeOracle(
    address,
    address,
    uint256
  ) external;

  function report(
    address,
    uint256,
    address,
    address
  ) external;

  function removeExpiredReports(address, uint256) external;

  function isOldestReportExpired(address token) external view returns (bool, address);

  function numRates(address) external view returns (uint256);

  function medianRate(address) external view returns (uint256, uint256);

  function numTimestamps(address) external view returns (uint256);

  function medianTimestamp(address) external view returns (uint256);

  function getOracles(address) external view returns (address[] memory);

  function getTimestamps(address token)
    external
    view
    returns (
      address[] memory,
      uint256[] memory,
      SortedLinkedListWithMedian.MedianRelation[] memory
    );
}
          

Compiler Settings

{"remappings":[":celo-foundry/=lib/celo-foundry/src/",":contracts/=contracts/",":ds-test/=lib/celo-foundry/lib/forge-std/lib/ds-test/src/",":forge-std-next/=lib/mento-core-2.2.0/lib/forge-std-next/src/",":forge-std/=lib/celo-foundry/lib/forge-std/src/",":mento-core-2.0.0/=lib/mento-core-2.0.0/contracts/",":mento-core-2.1.0/=lib/mento-core-2.1.0/contracts/",":mento-core-2.2.0/=lib/mento-core-2.2.0/contracts/",":openzeppelin-contracts-next/=lib/mento-core-2.2.0/lib/openzeppelin-contracts-next/",":openzeppelin-contracts-upgradeable/=lib/mento-core-2.2.0/lib/openzeppelin-contracts-upgradeable/",":openzeppelin-contracts/=lib/mento-core-2.0.0/lib/openzeppelin-contracts/contracts/",":openzeppelin-solidity/=lib/mento-core-2.0.0/lib/openzeppelin-contracts/",":test/=lib/mento-core-2.0.0/test/"],"optimizer":{"runs":10000,"enabled":true},"libraries":{"AddressSortedLinkedListWithMedian":"0xed477a99035d0c1e11369f1d7a4e587893cc002b","AddressLinkedList":"0x6200f54d73491d56b8d7a975c9ee18efb4d518df"},"compilationTarget":{"lib/mento-core-2.2.0/contracts/swap/BiPoolManager.sol":"BiPoolManager"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"bool","name":"test","internalType":"bool"}]},{"type":"event","name":"BreakerBoxUpdated","inputs":[{"type":"address","name":"newBreakerBox","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"BrokerUpdated","inputs":[{"type":"address","name":"newBroker","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"BucketsUpdated","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32","indexed":true},{"type":"uint256","name":"bucket0","internalType":"uint256","indexed":false},{"type":"uint256","name":"bucket1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ExchangeCreated","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32","indexed":true},{"type":"address","name":"asset0","internalType":"address","indexed":true},{"type":"address","name":"asset1","internalType":"address","indexed":true},{"type":"address","name":"pricingModule","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ExchangeDestroyed","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32","indexed":true},{"type":"address","name":"asset0","internalType":"address","indexed":true},{"type":"address","name":"asset1","internalType":"address","indexed":true},{"type":"address","name":"pricingModule","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PricingModulesUpdated","inputs":[{"type":"bytes32[]","name":"newIdentifiers","internalType":"bytes32[]","indexed":false},{"type":"address[]","name":"newAddresses","internalType":"address[]","indexed":false}],"anonymous":false},{"type":"event","name":"ReserveUpdated","inputs":[{"type":"address","name":"newReserve","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SortedOraclesUpdated","inputs":[{"type":"address","name":"newSortedOracles","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"CONSTANT_PRODUCT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"CONSTANT_SUM","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBreakerBox"}],"name":"breakerBox","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"broker","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"}],"name":"createExchange","inputs":[{"type":"tuple","name":"_exchange","internalType":"struct IBiPoolManager.PoolExchange","components":[{"type":"address","name":"asset0","internalType":"address"},{"type":"address","name":"asset1","internalType":"address"},{"type":"address","name":"pricingModule","internalType":"contract IPricingModule"},{"type":"uint256","name":"bucket0","internalType":"uint256"},{"type":"uint256","name":"bucket1","internalType":"uint256"},{"type":"uint256","name":"lastBucketUpdate","internalType":"uint256"},{"type":"tuple","name":"config","internalType":"struct IBiPoolManager.PoolConfig","components":[{"type":"tuple","name":"spread","internalType":"struct FixidityLib.Fraction","components":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"address","name":"referenceRateFeedID","internalType":"address"},{"type":"uint256","name":"referenceRateResetFrequency","internalType":"uint256"},{"type":"uint256","name":"minimumReports","internalType":"uint256"},{"type":"uint256","name":"stablePoolResetSize","internalType":"uint256"}]}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"destroyed","internalType":"bool"}],"name":"destroyExchange","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"uint256","name":"exchangeIdIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"exchangeIds","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"asset0","internalType":"address"},{"type":"address","name":"asset1","internalType":"address"},{"type":"address","name":"pricingModule","internalType":"contract IPricingModule"},{"type":"uint256","name":"bucket0","internalType":"uint256"},{"type":"uint256","name":"bucket1","internalType":"uint256"},{"type":"uint256","name":"lastBucketUpdate","internalType":"uint256"},{"type":"tuple","name":"config","internalType":"struct IBiPoolManager.PoolConfig","components":[{"type":"tuple","name":"spread","internalType":"struct FixidityLib.Fraction","components":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"address","name":"referenceRateFeedID","internalType":"address"},{"type":"uint256","name":"referenceRateResetFrequency","internalType":"uint256"},{"type":"uint256","name":"minimumReports","internalType":"uint256"},{"type":"uint256","name":"stablePoolResetSize","internalType":"uint256"}]}],"name":"exchanges","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32[]","name":"","internalType":"bytes32[]"}],"name":"getExchangeIds","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"_exchanges","internalType":"struct IExchangeProvider.Exchange[]","components":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address[]","name":"assets","internalType":"address[]"}]}],"name":"getExchanges","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"exchange","internalType":"struct IBiPoolManager.PoolExchange","components":[{"type":"address","name":"asset0","internalType":"address"},{"type":"address","name":"asset1","internalType":"address"},{"type":"address","name":"pricingModule","internalType":"contract IPricingModule"},{"type":"uint256","name":"bucket0","internalType":"uint256"},{"type":"uint256","name":"bucket1","internalType":"uint256"},{"type":"uint256","name":"lastBucketUpdate","internalType":"uint256"},{"type":"tuple","name":"config","internalType":"struct IBiPoolManager.PoolConfig","components":[{"type":"tuple","name":"spread","internalType":"struct FixidityLib.Fraction","components":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"address","name":"referenceRateFeedID","internalType":"address"},{"type":"uint256","name":"referenceRateResetFrequency","internalType":"uint256"},{"type":"uint256","name":"minimumReports","internalType":"uint256"},{"type":"uint256","name":"stablePoolResetSize","internalType":"uint256"}]}]}],"name":"getPoolExchange","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","name":"initialize","inputs":[{"type":"address","name":"_broker","internalType":"address"},{"type":"address","name":"_reserve","internalType":"contract IReserve"},{"type":"address","name":"_sortedOracles","internalType":"contract ISortedOracles"},{"type":"address","name":"_breakerBox","internalType":"contract IBreakerBox"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"initialized","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pricingModules","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IReserve"}],"name":"reserve","inputs":[]},{"type":"function","stateMutability":"nonpayable","name":"setBreakerBox","inputs":[{"type":"address","name":"_breakerBox","internalType":"contract IBreakerBox"}]},{"type":"function","stateMutability":"nonpayable","name":"setBroker","inputs":[{"type":"address","name":"_broker","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","name":"setPricingModules","inputs":[{"type":"bytes32[]","name":"identifiers","internalType":"bytes32[]"},{"type":"address[]","name":"modules","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","name":"setReserve","inputs":[{"type":"address","name":"_reserve","internalType":"contract IReserve"}]},{"type":"function","stateMutability":"nonpayable","name":"setSortedOracles","inputs":[{"type":"address","name":"_sortedOracles","internalType":"contract ISortedOracles"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ISortedOracles"}],"name":"sortedOracles","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapIn","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapOut","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenPrecisionMultipliers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
              

Contract Creation Code

0x60806040523480156200001157600080fd5b5060405162004241380380620042418339810160408190526200003491620000cf565b808062000049576000805460ff191660011790555b5060006200005f6001600160e01b03620000b816565b60008054610100600160a81b0319166101006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3505062000117565b3390565b8051620000c981620000fd565b92915050565b600060208284031215620000e257600080fd5b6000620000f08484620000bc565b949350505050565b151590565b6200010881620000f8565b81146200011457600080fd5b50565b61411a80620001276000396000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c8063abff011011610104578063d482dda6116100a2578063f2fde38b11610071578063f2fde38b146103cd578063f414c5e4146103e0578063f670dde1146103e8578063f8c8765e146103fb576101da565b8063d482dda61461036c578063dc162e3614610392578063e46eb5b6146103a7578063eaaba701146103ba576101da565b8063c0ad52d5116100de578063c0ad52d514610341578063cd3293de14610349578063cd8513ba14610351578063d3385d0514610359576101da565b8063abff011014610313578063b08986911461031b578063bf0d02131461032e576101da565b8063450154211161017c5780638da5cb5b1161014b5780638da5cb5b146102d05780638f32d59b146102e557806393c7e3bc146102ed5780639cecc80a14610300576101da565b8063450154211461028f57806345b2b467146102a25780634afb215e146102b5578063715018a6146102c8576101da565b80631e2e3a6b116101b85780631e2e3a6b14610227578063278488a41461023c57806338b1e9f71461025c57806342bfc99c1461027c576101da565b8063040bbd35146101df578063132e8aa7146101f4578063158ef93e14610212575b600080fd5b6101f26101ed366004612e82565b61040e565b005b6101fc6104cf565b6040516102099190613d62565b60405180910390f35b61021a6104de565b6040516102099190613d46565b61022f6104e7565b6040516102099190613d35565b61024f61024a366004612ddf565b6106b0565b6040516102099190613f61565b61026f61026a366004612ddf565b610793565b6040516102099190613d54565b61026f61028a366004612dfd565b6107b1565b6101f261029d366004612d17565b610939565b61026f6102b0366004612c98565b610a53565b6101f26102c3366004612e82565b610a65565b6101f2610b11565b6102d8610b9b565b6040516102099190613c88565b61021a610baf565b61026f6102fb366004612dfd565b610bd8565b6101f261030e366004612e82565b610c63565b6102d8610d0f565b61026f610329366004612ed5565b610d1e565b6101f261033c366004612c98565b6113b4565b61026f611460565b6101fc611488565b61026f611497565b61026f610367366004612dfd565b6114a6565b61037f61037a366004612ddf565b611621565b6040516102099796959493929190613c96565b61039a6116ad565b6040516102099190613d24565b61021a6103b5366004612e52565b611705565b6102d86103c8366004612ddf565b611a65565b6101f26103db366004612c98565b611a80565b6101fc611ab0565b61026f6103f6366004612dfd565b611abf565b6101f2610409366004612cb6565b611b3e565b610416610baf565b61043b5760405162461bcd60e51b815260040161043290613ea1565b60405180910390fd5b6001600160a01b0381166104615760405162461bcd60e51b815260040161043290613f21565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556040517f21921b3b46ef2c939e85d6a14410c6e3b9ce132b66e944357ff4f789f68e00e2906104c4908390613c88565b60405180910390a150565b6006546001600160a01b031681565b60005460ff1681565b6003546040805182815260208084028201019091526060919081801561052757816020015b61051461296d565b81526020019060019003908161050c5790505b50915060005b818110156106ab576003818154811061054257fe5b906000526020600020015483828151811061055957fe5b60209081029190910101515260408051600280825260608201909252908160200160208202803883390190505083828151811061059257fe5b60200260200101516020018190525060026000600383815481106105b257fe5b9060005260206000200154815260200190815260200160002060000160009054906101000a90046001600160a01b03168382815181106105ee57fe5b60200260200101516020015160008151811061060657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600260006003838154811061063757fe5b9060005260206000200154815260200190815260200160002060010160009054906101000a90046001600160a01b031683828151811061067357fe5b60200260200101516020015160018151811061068b57fe5b6001600160a01b039092166020928302919091019091015260010161052d565b505090565b6106b8612985565b50600081815260026020818152604092839020835160e08101855281546001600160a01b03908116825260018301548116828501529382015484168186015260038201546060808301919091526004830154608080840191909152600584015460a080850191909152875160c08181018a5260068701549282019283529181526007860154881696810196909652600885015497860197909752600984015491850191909152600a90920154918301919091529283015281511661078e5760405162461bcd60e51b815260040161043290613ef1565b919050565b600381815481106107a057fe5b600091825260209091200154905081565b6001546000906001600160a01b031633146107de5760405162461bcd60e51b815260040161043290613e31565b6107e6612985565b6107ef866106b0565b60055460c0820151602001516040517fc0b05da00000000000000000000000000000000000000000000000000000000081529293506000926001600160a01b039092169163c0b05da09161084591600401613c88565b60206040518083038186803b15801561085d57600080fd5b505afa158015610871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108959190810190612f61565b60ff16146108b55760405162461bcd60e51b815260040161043290613d81565b6001600160a01b0385166000908152600760205260408120546108df90859063ffffffff611bbf16565b90506000806108f084898986611c02565b9150915061090289858a868686611e0a565b6001600160a01b03871660009081526007602052604090205461092c90839063ffffffff611f2b16565b9998505050505050505050565b610941610baf565b61095d5760405162461bcd60e51b815260040161043290613ea1565b82811461097c5760405162461bcd60e51b815260040161043290613f51565b60005b83811015610a0f5782828281811061099357fe5b90506020020160206109a89190810190612c98565b600860008787858181106109b857fe5b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905560010161097f565b507f84557d458d60fcb29d9a10a8f28861c76847f7ef6450a9266a228a6f3a590baa84848484604051610a459493929190613cf3565b60405180910390a150505050565b60076020526000908152604090205481565b610a6d610baf565b610a895760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b038116610aaf5760405162461bcd60e51b815260040161043290613f01565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f590fd633a008765ce9e65e8081adfba311e99e11b958a5ecb5000ea3355f735390600090a250565b610b19610baf565b610b355760405162461bcd60e51b815260040161043290613ea1565b600080546040516101009091046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b60005461010090046001600160a01b031690565b6000805461010090046001600160a01b0316610bc9611f6d565b6001600160a01b031614905090565b6000610be2612985565b610beb866106b0565b6001600160a01b03861660009081526007602052604081205491925090610c1990859063ffffffff611bbf16565b90506000610c2983888885611c02565b506001600160a01b038716600090815260076020526040902054909150610c5790829063ffffffff611f2b16565b98975050505050505050565b610c6b610baf565b610c875760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b038116610cad5760405162461bcd60e51b815260040161043290613f11565b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f0b248ab246a87e452fbedee8dc12dfc53e3f3bfdc6920999062c56dea4ab522a90600090a250565b6001546001600160a01b031681565b6000610d28610baf565b610d445760405162461bcd60e51b815260040161043290613ea1565b610d4c612985565b610d5b36849003840184612ef4565b60408101519091506001600160a01b0316610d885760405162461bcd60e51b815260040161043290613e71565b80516001600160a01b0316610daf5760405162461bcd60e51b815260040161043290613dd1565b60208101516001600160a01b0316610dd95760405162461bcd60e51b815260040161043290613ec1565b80602001516001600160a01b031681600001516001600160a01b03161415610e135760405162461bcd60e51b815260040161043290613eb1565b80604001516001600160a01b031660086000610e2e84611f71565b81526020810191909152604001600020546001600160a01b031614610e655760405162461bcd60e51b815260040161043290613e91565b80600001516001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015610ea257600080fd5b505afa158015610eb6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ede9190810190612ea0565b81602001516001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015610f1b57600080fd5b505afa158015610f2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f579190810190612ea0565b82604001516001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015610f9457600080fd5b505afa158015610fa8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fd09190810190612ea0565b604051602001610fe293929190613c4e565b60408051601f198184030181529181528151602092830120600081815260029093529120549092506001600160a01b0316156110305760405162461bcd60e51b815260040161043290613e01565b61103981612019565b60008061104583612289565b915091508183606001818152505080836080018181525050600083600001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110d49190810190612f61565b60ff169050600084602001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561111857600080fd5b505afa15801561112c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111509190810190612f61565b60ff16905060128211156111765760405162461bcd60e51b815260040161043290613e11565b60128111156111975760405162461bcd60e51b815260040161043290613e51565b81601203600a0a6007600087600001516001600160a01b03166001600160a01b031681526020019081526020016000208190555080601203600a0a6007600087602001516001600160a01b03166001600160a01b0316815260200190815260200160002081905550846002600088815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600301556080820151816004015560a0820151816005015560c08201518160060160008201518160000160008201518160000155505060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030155608082015181600401555050905050600386908060018154018082558091505090600182039060005260206000200160009091929091909150555084602001516001600160a01b031685600001516001600160a01b0316877fb374789237d43aed0f75b4c50a45793fd70e354bb96eecd573f35f5d509d78a188604001516040516113a29190613c88565b60405180910390a45050505050919050565b6113bc610baf565b6113d85760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b0381166113fe5760405162461bcd60e51b815260040161043290613f41565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f865dab7821134b6eb27cba259b40e33bbc1b898e970a535a18a83147f380a51f90600090a250565b60405160200161146f90613c7d565b6040516020818303038152906040528051906020012081565b6004546001600160a01b031681565b60405160200161146f90613c72565b6001546000906001600160a01b031633146114d35760405162461bcd60e51b815260040161043290613e31565b6114db612985565b6114e4866106b0565b60055460c0820151602001516040517fc0b05da00000000000000000000000000000000000000000000000000000000081529293506000926001600160a01b039092169163c0b05da09161153a91600401613c88565b60206040518083038186803b15801561155257600080fd5b505afa158015611566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061158a9190810190612f61565b60ff16146115aa5760405162461bcd60e51b815260040161043290613d81565b6001600160a01b0384166000908152600760205260408120546115d490859063ffffffff611bbf16565b90506000806115e5848989866122d9565b915091506115f789858a858786611e0a565b6001600160a01b03881660009081526007602052604090205461092c90839063ffffffff611f2b16565b6002602081815260009283526040928390208054600182015493820154600383015460048401546005850154885160c081018a52600687015460a08201908152815260078701546001600160a01b0390811698820198909852600887015499810199909952600986015460608a0152600a90950154608089015292851696958516959190941693929087565b606060038054806020026020016040519081016040528092919081815260200182805480156116fb57602002820191906000526020600020905b8154815260200190600101908083116116e7575b5050505050905090565b600061170f610baf565b61172b5760405162461bcd60e51b815260040161043290613ea1565b600354821061174c5760405162461bcd60e51b815260040161043290613de1565b826003838154811061175a57fe5b9060005260206000200154146117825760405162461bcd60e51b815260040161043290613ed1565b61178a612985565b600260008581526020019081526020016000206040518060e00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600382015481526020016004820154815260200160058201548152602001600682016040518060a00160405290816000820160405180602001604052908160008201548152505081526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600282015481526020016003820154815260200160048201548152505081525050905060026000858152602001908152602001600020600080820160006101000a8154906001600160a01b0302191690556001820160006101000a8154906001600160a01b0302191690556002820160006101000a8154906001600160a01b03021916905560038201600090556004820160009055600582016000905560068201600080820160008082016000905550506001820160006101000a8154906001600160a01b0302191690556002820160009055600382016000905560048201600090555050505060036119b360016003805490506123eb90919063ffffffff16565b815481106119bd57fe5b9060005260206000200154600384815481106119d557fe5b60009182526020909120015560038054806119ec57fe5b600190038181906000526020600020016000905590556001915080602001516001600160a01b031681600001516001600160a01b0316857fadbbec6c203cb0248e89fe6d15ad651061a9d5203a1ab0273256e2b7decffa898460400151604051611a569190613c88565b60405180910390a45092915050565b6008602052600090815260409020546001600160a01b031681565b611a88610baf565b611aa45760405162461bcd60e51b815260040161043290613ea1565b611aad8161242d565b50565b6005546001600160a01b031681565b6000611ac9612985565b611ad2866106b0565b6001600160a01b03851660009081526007602052604081205491925090611b0090859063ffffffff611bbf16565b90506000611b10838888856122d9565b506001600160a01b038816600090815260076020526040902054909150610c5790829063ffffffff611f2b16565b60005460ff1615611b615760405162461bcd60e51b815260040161043290613da1565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611b953361242d565b611b9e846113b4565b611ba783610c63565b611bb082610a65565b611bb98161040e565b50505050565b600082611bce57506000611bfc565b82820282848281611bdb57fe5b0414611bf95760405162461bcd60e51b815260040161043290613e81565b90505b92915050565b60008084848780600001516001600160a01b0316836001600160a01b0316148015611c42575080602001516001600160a01b0316826001600160a01b0316145b80611c79575080602001516001600160a01b0316836001600160a01b0316148015611c79575080516001600160a01b038381169116145b611c955760405162461bcd60e51b815260040161043290613f31565b611c9e896124d0565b8151919a5094506001600160a01b0389811691161415611d5d5788604001516001600160a01b03166352707d8c8a606001518b60800151611ce68d60c0015160000151612508565b8a6040518563ffffffff1660e01b8152600401611d069493929190613f8b565b60206040518083038186803b158015611d1e57600080fd5b505afa158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d569190810190612f13565b9450611dfe565b88604001516001600160a01b03166352707d8c8a608001518b60600151611d8b8d60c0015160000151612508565b8a6040518563ffffffff1660e01b8152600401611dab9493929190613f8b565b60206040518083038186803b158015611dc357600080fd5b505afa158015611dd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfb9190810190612f13565b94505b50505094509492505050565b8015611e6e57426002600088815260200190815260200160002060050181905550857f949f5db193cbfa01f2d443b1c656bfede15497de0d86838cea089bd60c43834286606001518760800151604051611e65929190613f70565b60405180910390a25b611e778561250c565b15611eff5784516001600160a01b0385811691161415611eca576060850151611ea6908463ffffffff61254316565b60608601526080850151611ec0908363ffffffff6123eb16565b6080860152611eff565b6060850151611edf908363ffffffff6123eb16565b60608601526080850151611ef9908463ffffffff61254316565b60808601525b505050606082015160009384526002602052604090932060038101939093555060800151600490910155565b6000611bf983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612568565b3390565b600081604001516001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015611fb057600080fd5b505afa158015611fc4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611fec9190810190612ea0565b604051602001611ffc9190613c42565b604051602081830303815290604052805190602001209050919050565b6004805482516040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390921692634f8e6e2392612062929101613c88565b60206040518083038186803b15801561207a57600080fd5b505afa15801561208e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120b29190810190612d87565b6120ce5760405162461bcd60e51b815260040161043290613d91565b6004805460208301516040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390921692634f8e6e239261211a929101613c88565b60206040518083038186803b15801561213257600080fd5b505afa158015612146573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061216a9190810190612d87565b8061220c57506004805460208301516040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169263cae182fe926121bc929101613c88565b60206040518083038186803b1580156121d457600080fd5b505afa1580156121e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061220c9190810190612d87565b6122285760405162461bcd60e51b815260040161043290613e21565b60c08101515161223f9061223a6125a1565b6125c5565b61225b5760405162461bcd60e51b815260040161043290613e61565b60c0810151602001516001600160a01b0316611aad5760405162461bcd60e51b815260040161043290613ee1565b6000808260c001516080015191506000806122ab8560c00151602001516125cd565b90925090506122d0826122c4838763ffffffff611bbf16565b9063ffffffff611f2b16565b92505050915091565b60008084848780600001516001600160a01b0316836001600160a01b0316148015612319575080602001516001600160a01b0316826001600160a01b0316145b80612350575080602001516001600160a01b0316836001600160a01b0316148015612350575080516001600160a01b038381169116145b61236c5760405162461bcd60e51b815260040161043290613f31565b612375896124d0565b8151919a5094506001600160a01b03898116911614156123bd5788604001516001600160a01b031663571fd0128a606001518b60800151611ce68d60c0015160000151612508565b88604001516001600160a01b031663571fd0128a608001518b60600151611d8b8d60c0015160000151612508565b6000611bf983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061268c565b6001600160a01b0381166124535760405162461bcd60e51b815260040161043290613db1565b600080546040516001600160a01b038085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b6124d8612985565b60006124e3836126b8565b156124ff576124f183612289565b608085015260608401525060015b8291505b915091565b5190565b600060405160200161251d90613c72565b6040516020818303038152906040528051906020012061253c83611f71565b1492915050565b600082820183811015611bf95760405162461bcd60e51b815260040161043290613df1565b600081836125895760405162461bcd60e51b81526004016104329190613d70565b50600083858161259557fe5b049150505b9392505050565b6125a96129e2565b50604080516020810190915269d3c21bcecceda1000000815290565b519051111590565b6006546040517fef90e1b000000000000000000000000000000000000000000000000000000000815260009182916001600160a01b039091169063ef90e1b09061261b908690600401613c88565b604080518083038186803b15801561263257600080fd5b505afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061266a9190810190612f31565b9092509050806125035760405162461bcd60e51b815260040161043290613e41565b600081848411156126b05760405162461bcd60e51b81526004016104329190613d70565b505050900390565b6000806126c483612729565b90506126cf8361295c565b156126f157806126f15760405162461bcd60e51b815260040161043290613dc1565b60006127128460c00151604001518560a0015161254390919063ffffffff16565b42101590508080156127215750815b949350505050565b60065460c0820151602001516040517fffe736bf00000000000000000000000000000000000000000000000000000000815260009283926001600160a01b039091169163ffe736bf9161277e91600401613c88565b604080518083038186803b15801561279557600080fd5b505afa1580156127a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cd9190810190612da5565b5060c084015160608101516006546020909201516040517fbbc66a9400000000000000000000000000000000000000000000000000000000815293945060009391926001600160a01b03169163bbc66a949161282b91600401613c88565b60206040518083038186803b15801561284357600080fd5b505afa158015612857573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061287b9190810190612f13565b10159050600061289c8560c0015160400151426123eb90919063ffffffff16565b60065460c0870151602001516040517f071b48fc0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163071b48fc916128ec91600401613c88565b60206040518083038186803b15801561290457600080fd5b505afa158015612918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061293c9190810190612f13565b1190508215801561294a5750815b80156129535750805b95945050505050565b600060405160200161251d90613c7d565b60408051808201909152600081526060602082015290565b6040518060e0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016129dd6129f5565b905290565b6040518060200160405280600081525090565b6040518060a00160405280612a086129e2565b815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8035611bfc8161409f565b8051611bfc8161409f565b60008083601f840112612a5b57600080fd5b50813567ffffffffffffffff811115612a7357600080fd5b602083019150836020820283011115612a8b57600080fd5b9250929050565b8051611bfc816140b3565b8035611bfc816140bc565b8035611bfc816140c5565b600082601f830112612ac457600080fd5b8151612ad7612ad282613fe7565b613fc0565b91508082526020830160208301858383011115612af357600080fd5b612afe838284614069565b50505092915050565b600060208284031215612b1957600080fd5b612b236020613fc0565b90506000612b318484612a9d565b82525092915050565b600060a08284031215612b4c57600080fd5b612b5660a0613fc0565b90506000612b648484612b07565b8252506020612b7584848301612a33565b6020830152506040612b8984828501612a9d565b6040830152506060612b9d84828501612a9d565b6060830152506080612bb184828501612a9d565b60808301525092915050565b60006101608284031215612bd057600080fd5b50919050565b60006101608284031215612be957600080fd5b612bf360e0613fc0565b90506000612c018484612a33565b8252506020612c1284848301612a33565b6020830152506040612c2684828501612aa8565b6040830152506060612c3a84828501612a9d565b6060830152506080612c4e84828501612a9d565b60808301525060a0612c6284828501612a9d565b60a08301525060c0612c7684828501612b3a565b60c08301525092915050565b8051611bfc816140bc565b8051611bfc816140ce565b600060208284031215612caa57600080fd5b60006127218484612a33565b60008060008060808587031215612ccc57600080fd5b6000612cd88787612a33565b9450506020612ce987828801612aa8565b9350506040612cfa87828801612aa8565b9250506060612d0b87828801612aa8565b91505092959194509250565b60008060008060408587031215612d2d57600080fd5b843567ffffffffffffffff811115612d4457600080fd5b612d5087828801612a49565b9450945050602085013567ffffffffffffffff811115612d6f57600080fd5b612d7b87828801612a49565b95989497509550505050565b600060208284031215612d9957600080fd5b60006127218484612a92565b60008060408385031215612db857600080fd5b6000612dc48585612a92565b9250506020612dd585828601612a3e565b9150509250929050565b600060208284031215612df157600080fd5b60006127218484612a9d565b60008060008060808587031215612e1357600080fd5b6000612e1f8787612a9d565b9450506020612e3087828801612a33565b9350506040612e4187828801612a33565b9250506060612d0b87828801612a9d565b60008060408385031215612e6557600080fd5b6000612e718585612a9d565b9250506020612dd585828601612a9d565b600060208284031215612e9457600080fd5b60006127218484612aa8565b600060208284031215612eb257600080fd5b815167ffffffffffffffff811115612ec957600080fd5b61272184828501612ab3565b60006101608284031215612ee857600080fd5b60006127218484612bbd565b60006101608284031215612f0757600080fd5b60006127218484612bd6565b600060208284031215612f2557600080fd5b60006127218484612c82565b60008060408385031215612f4457600080fd5b6000612f508585612c82565b9250506020612dd585828601612c82565b600060208284031215612f7357600080fd5b60006127218484612c8d565b6000612f8b8383612fab565b505060200190565b6000612f8b8383613177565b6000611bf98383613b24565b612fb481614030565b82525050565b6000612fc68385614018565b9350612fd18261400f565b8060005b8581101561300757612fe78284614021565b612ff18882612f7f565b9750612ffc83614012565b925050600101612fd5565b509495945050505050565b600061301d82612508565b6130278185614018565b935061303283614012565b8060005b8381101561300757815161304a8882612f7f565b975061305583614012565b925050600101613036565b600061306c8385614018565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561309b57600080fd5b6020830292506130ac83858461405d565b50500190565b60006130bd82612508565b6130c78185614018565b93506130d283614012565b8060005b838110156130075781516130ea8882612f93565b97506130f583614012565b9250506001016130d6565b600061310b82612508565b6131158185614018565b93508360208202850161312785614012565b8060005b8581101561316157848403895281516131448582612f9f565b945061314f83614012565b60209a909a019992505060010161312b565b5091979650505050505050565b612fb48161403b565b612fb48161400f565b612fb481614040565b600061319482612508565b61319e8185614018565b93506131ae818560208601614069565b6131b781614095565b9093019392505050565b60006131cc82612508565b6131d6818561078e565b93506131e6818560208601614069565b9290920192915050565b60006131fd602c83614018565b7f54726164696e672069732073757370656e64656420666f72207468697320726581527f666572656e636520726174650000000000000000000000000000000000000000602082015260400192915050565b600061325c603383614018565b7f617373657430206d757374206265206120737461626c6520726567697374657281527f6564207769746820746865207265736572766500000000000000000000000000602082015260400192915050565b60006132bb601c83614018565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b60006132f4602683614018565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613353600f83614018565b7f6e6f2076616c6964206d656469616e0000000000000000000000000000000000815260200192915050565b600061338c601283614018565b7f617373657430206d757374206265207365740000000000000000000000000000815260200192915050565b60006133c5601c83614018565b7f65786368616e67654964496e646578206e6f7420696e2072616e676500000000815260200192915050565b60006133fe601b83614018565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000613437600f8361078e565b7f436f6e7374616e7450726f6475637400000000000000000000000000000000008152600f0192915050565b6000613470603983614018565b7f416e2065786368616e676520776974682074686520737065636966696564206181527f737365747320616e642065786368616e67652065786973747300000000000000602082015260400192915050565b60006134cf601d83614018565b7f61737365743020646563696d616c73206d757374206265203c3d203138000000815260200192915050565b6000613508602583614018565b7f617373657431206d757374206265206120737461626c65206f7220636f6c6c6181527f746572616c000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613567601883614018565b7f43616c6c6572206973206e6f74207468652042726f6b65720000000000000000815260200192915050565b60006135a0603083614018565b7f65786368616e676520726174652064656e6f6d696e61746f72206d757374206281527f652067726561746572207468616e203000000000000000000000000000000000602082015260400192915050565b60006135ff601d83614018565b7f61737365743120646563696d616c73206d757374206265203c3d203138000000815260200192915050565b6000613638602683614018565b7f737072656164206d757374206265206c657373207468616e206f72206571756181527f6c20746f20310000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613697601983614018565b7f70726963696e674d6f64756c65206d7573742062652073657400000000000000815260200192915050565b60006136d0602183614018565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061372f601583614018565b7f696e76616c69642070726963696e674d6f64756c650000000000000000000000815260200192915050565b6000613768602083614018565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b60006137a1602283614018565b7f65786368616e6765206173736574732063616e2774206265206964656e74696381527f616c000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613800601283614018565b7f617373657431206d757374206265207365740000000000000000000000000000815260200192915050565b6000613839600b8361078e565b7f436f6e7374616e7453756d0000000000000000000000000000000000000000008152600b0192915050565b6000613872602183614018565b7f65786368616e6765496420617420696e64657820646f65736e2774206d61746381527f6800000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006138d1601f83614018565b7f7265666572656e636552617465466565644944206d7573742062652073657400815260200192915050565b600061390a603083614018565b7f416e2065786368616e676520776974682074686520737065636966696564206981527f6420646f6573206e6f7420657869737400000000000000000000000000000000602082015260400192915050565b6000613969602183614018565b7f536f727465644f7261636c65732061646472657373206d75737420626520736581527f7400000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006139c8601b83614018565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000613a01601e83614018565b7f427265616b6572426f782061646472657373206d757374206265207365740000815260200192915050565b6000613a3a602883614018565b7f746f6b656e496e20616e6420746f6b656e4f7574206d757374206d617463682081527f65786368616e6765000000000000000000000000000000000000000000000000602082015260400192915050565b6000613a99601a83614018565b7f42726f6b65722061646472657373206d75737420626520736574000000000000815260200192915050565b6000613ad2602f83614018565b7f6964656e7469666965727320616e64206d6f64756c6573206d7573742062652081527f7468652073616d65206c656e6774680000000000000000000000000000000000602082015260400192915050565b80516000906040840190613b388582613177565b50602083015184820360208601526129538282613012565b80516020830190611bb98482613177565b805160a0830190613b728482613b50565b506020820151613b856020850182612fab565b506040820151613b986040850182613177565b506060820151613bab6060850182613177565b506080820151611bb96080850182613177565b8051610160830190613bd08482612fab565b506020820151613be36020850182612fab565b506040820151613bf66040850182613180565b506060820151613c096060850182613177565b506080820151613c1c6080850182613177565b5060a0820151613c2f60a0850182613177565b5060c0820151611bb960c0850182613b61565b600061259a82846131c1565b6000613c5a82866131c1565b9150613c6682856131c1565b915061295382846131c1565b6000611bfc8261342a565b6000611bfc8261382c565b60208101611bfc8284612fab565b6101608101613ca5828a612fab565b613cb26020830189612fab565b613cbf6040830188613180565b613ccc6060830187613177565b613cd96080830186613177565b613ce660a0830185613177565b610c5760c0830184613b61565b60408082528101613d05818688613060565b90508181036020830152613d1a818486612fba565b9695505050505050565b60208082528101611bf981846130b2565b60208082528101611bf98184613100565b60208101611bfc828461316e565b60208101611bfc8284613177565b60208101611bfc8284613180565b60208082528101611bf98184613189565b60208082528101611bfc816131f0565b60208082528101611bfc8161324f565b60208082528101611bfc816132ae565b60208082528101611bfc816132e7565b60208082528101611bfc81613346565b60208082528101611bfc8161337f565b60208082528101611bfc816133b8565b60208082528101611bfc816133f1565b60208082528101611bfc81613463565b60208082528101611bfc816134c2565b60208082528101611bfc816134fb565b60208082528101611bfc8161355a565b60208082528101611bfc81613593565b60208082528101611bfc816135f2565b60208082528101611bfc8161362b565b60208082528101611bfc8161368a565b60208082528101611bfc816136c3565b60208082528101611bfc81613722565b60208082528101611bfc8161375b565b60208082528101611bfc81613794565b60208082528101611bfc816137f3565b60208082528101611bfc81613865565b60208082528101611bfc816138c4565b60208082528101611bfc816138fd565b60208082528101611bfc8161395c565b60208082528101611bfc816139bb565b60208082528101611bfc816139f4565b60208082528101611bfc81613a2d565b60208082528101611bfc81613a8c565b60208082528101611bfc81613ac5565b6101608101611bfc8284613bbe565b60408101613f7e8285613177565b61259a6020830184613177565b60808101613f998287613177565b613fa66020830186613177565b613fb36040830185613177565b6129536060830184613177565b60405181810167ffffffffffffffff81118282101715613fdf57600080fd5b604052919050565b600067ffffffffffffffff821115613ffe57600080fd5b506020601f91909101601f19160190565b90565b60200190565b90815260200190565b6000611bf96020840184612a33565b6000611bfc8261404b565b151590565b6000611bfc82614030565b6001600160a01b031690565b60ff1690565b82818337506000910152565b60005b8381101561408457818101518382015260200161406c565b83811115611bb95750506000910152565b601f01601f191690565b6140a881614030565b8114611aad57600080fd5b6140a88161403b565b6140a88161400f565b6140a881614040565b6140a88161405756fea365627a7a7231582051ffc7b04e35ba593e770fa68c52682f6ce8a1563a825d27e055b8f924b7dca26c6578706572696d656e74616cf564736f6c634300051100400000000000000000000000000000000000000000000000000000000000000000

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101da5760003560e01c8063abff011011610104578063d482dda6116100a2578063f2fde38b11610071578063f2fde38b146103cd578063f414c5e4146103e0578063f670dde1146103e8578063f8c8765e146103fb576101da565b8063d482dda61461036c578063dc162e3614610392578063e46eb5b6146103a7578063eaaba701146103ba576101da565b8063c0ad52d5116100de578063c0ad52d514610341578063cd3293de14610349578063cd8513ba14610351578063d3385d0514610359576101da565b8063abff011014610313578063b08986911461031b578063bf0d02131461032e576101da565b8063450154211161017c5780638da5cb5b1161014b5780638da5cb5b146102d05780638f32d59b146102e557806393c7e3bc146102ed5780639cecc80a14610300576101da565b8063450154211461028f57806345b2b467146102a25780634afb215e146102b5578063715018a6146102c8576101da565b80631e2e3a6b116101b85780631e2e3a6b14610227578063278488a41461023c57806338b1e9f71461025c57806342bfc99c1461027c576101da565b8063040bbd35146101df578063132e8aa7146101f4578063158ef93e14610212575b600080fd5b6101f26101ed366004612e82565b61040e565b005b6101fc6104cf565b6040516102099190613d62565b60405180910390f35b61021a6104de565b6040516102099190613d46565b61022f6104e7565b6040516102099190613d35565b61024f61024a366004612ddf565b6106b0565b6040516102099190613f61565b61026f61026a366004612ddf565b610793565b6040516102099190613d54565b61026f61028a366004612dfd565b6107b1565b6101f261029d366004612d17565b610939565b61026f6102b0366004612c98565b610a53565b6101f26102c3366004612e82565b610a65565b6101f2610b11565b6102d8610b9b565b6040516102099190613c88565b61021a610baf565b61026f6102fb366004612dfd565b610bd8565b6101f261030e366004612e82565b610c63565b6102d8610d0f565b61026f610329366004612ed5565b610d1e565b6101f261033c366004612c98565b6113b4565b61026f611460565b6101fc611488565b61026f611497565b61026f610367366004612dfd565b6114a6565b61037f61037a366004612ddf565b611621565b6040516102099796959493929190613c96565b61039a6116ad565b6040516102099190613d24565b61021a6103b5366004612e52565b611705565b6102d86103c8366004612ddf565b611a65565b6101f26103db366004612c98565b611a80565b6101fc611ab0565b61026f6103f6366004612dfd565b611abf565b6101f2610409366004612cb6565b611b3e565b610416610baf565b61043b5760405162461bcd60e51b815260040161043290613ea1565b60405180910390fd5b6001600160a01b0381166104615760405162461bcd60e51b815260040161043290613f21565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556040517f21921b3b46ef2c939e85d6a14410c6e3b9ce132b66e944357ff4f789f68e00e2906104c4908390613c88565b60405180910390a150565b6006546001600160a01b031681565b60005460ff1681565b6003546040805182815260208084028201019091526060919081801561052757816020015b61051461296d565b81526020019060019003908161050c5790505b50915060005b818110156106ab576003818154811061054257fe5b906000526020600020015483828151811061055957fe5b60209081029190910101515260408051600280825260608201909252908160200160208202803883390190505083828151811061059257fe5b60200260200101516020018190525060026000600383815481106105b257fe5b9060005260206000200154815260200190815260200160002060000160009054906101000a90046001600160a01b03168382815181106105ee57fe5b60200260200101516020015160008151811061060657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600260006003838154811061063757fe5b9060005260206000200154815260200190815260200160002060010160009054906101000a90046001600160a01b031683828151811061067357fe5b60200260200101516020015160018151811061068b57fe5b6001600160a01b039092166020928302919091019091015260010161052d565b505090565b6106b8612985565b50600081815260026020818152604092839020835160e08101855281546001600160a01b03908116825260018301548116828501529382015484168186015260038201546060808301919091526004830154608080840191909152600584015460a080850191909152875160c08181018a5260068701549282019283529181526007860154881696810196909652600885015497860197909752600984015491850191909152600a90920154918301919091529283015281511661078e5760405162461bcd60e51b815260040161043290613ef1565b919050565b600381815481106107a057fe5b600091825260209091200154905081565b6001546000906001600160a01b031633146107de5760405162461bcd60e51b815260040161043290613e31565b6107e6612985565b6107ef866106b0565b60055460c0820151602001516040517fc0b05da00000000000000000000000000000000000000000000000000000000081529293506000926001600160a01b039092169163c0b05da09161084591600401613c88565b60206040518083038186803b15801561085d57600080fd5b505afa158015610871573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108959190810190612f61565b60ff16146108b55760405162461bcd60e51b815260040161043290613d81565b6001600160a01b0385166000908152600760205260408120546108df90859063ffffffff611bbf16565b90506000806108f084898986611c02565b9150915061090289858a868686611e0a565b6001600160a01b03871660009081526007602052604090205461092c90839063ffffffff611f2b16565b9998505050505050505050565b610941610baf565b61095d5760405162461bcd60e51b815260040161043290613ea1565b82811461097c5760405162461bcd60e51b815260040161043290613f51565b60005b83811015610a0f5782828281811061099357fe5b90506020020160206109a89190810190612c98565b600860008787858181106109b857fe5b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905560010161097f565b507f84557d458d60fcb29d9a10a8f28861c76847f7ef6450a9266a228a6f3a590baa84848484604051610a459493929190613cf3565b60405180910390a150505050565b60076020526000908152604090205481565b610a6d610baf565b610a895760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b038116610aaf5760405162461bcd60e51b815260040161043290613f01565b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f590fd633a008765ce9e65e8081adfba311e99e11b958a5ecb5000ea3355f735390600090a250565b610b19610baf565b610b355760405162461bcd60e51b815260040161043290613ea1565b600080546040516101009091046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b60005461010090046001600160a01b031690565b6000805461010090046001600160a01b0316610bc9611f6d565b6001600160a01b031614905090565b6000610be2612985565b610beb866106b0565b6001600160a01b03861660009081526007602052604081205491925090610c1990859063ffffffff611bbf16565b90506000610c2983888885611c02565b506001600160a01b038716600090815260076020526040902054909150610c5790829063ffffffff611f2b16565b98975050505050505050565b610c6b610baf565b610c875760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b038116610cad5760405162461bcd60e51b815260040161043290613f11565b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f0b248ab246a87e452fbedee8dc12dfc53e3f3bfdc6920999062c56dea4ab522a90600090a250565b6001546001600160a01b031681565b6000610d28610baf565b610d445760405162461bcd60e51b815260040161043290613ea1565b610d4c612985565b610d5b36849003840184612ef4565b60408101519091506001600160a01b0316610d885760405162461bcd60e51b815260040161043290613e71565b80516001600160a01b0316610daf5760405162461bcd60e51b815260040161043290613dd1565b60208101516001600160a01b0316610dd95760405162461bcd60e51b815260040161043290613ec1565b80602001516001600160a01b031681600001516001600160a01b03161415610e135760405162461bcd60e51b815260040161043290613eb1565b80604001516001600160a01b031660086000610e2e84611f71565b81526020810191909152604001600020546001600160a01b031614610e655760405162461bcd60e51b815260040161043290613e91565b80600001516001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015610ea257600080fd5b505afa158015610eb6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ede9190810190612ea0565b81602001516001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b158015610f1b57600080fd5b505afa158015610f2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f579190810190612ea0565b82604001516001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015610f9457600080fd5b505afa158015610fa8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fd09190810190612ea0565b604051602001610fe293929190613c4e565b60408051601f198184030181529181528151602092830120600081815260029093529120549092506001600160a01b0316156110305760405162461bcd60e51b815260040161043290613e01565b61103981612019565b60008061104583612289565b915091508183606001818152505080836080018181525050600083600001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110d49190810190612f61565b60ff169050600084602001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561111857600080fd5b505afa15801561112c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111509190810190612f61565b60ff16905060128211156111765760405162461bcd60e51b815260040161043290613e11565b60128111156111975760405162461bcd60e51b815260040161043290613e51565b81601203600a0a6007600087600001516001600160a01b03166001600160a01b031681526020019081526020016000208190555080601203600a0a6007600087602001516001600160a01b03166001600160a01b0316815260200190815260200160002081905550846002600088815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600301556080820151816004015560a0820151816005015560c08201518160060160008201518160000160008201518160000155505060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015560608201518160030155608082015181600401555050905050600386908060018154018082558091505090600182039060005260206000200160009091929091909150555084602001516001600160a01b031685600001516001600160a01b0316877fb374789237d43aed0f75b4c50a45793fd70e354bb96eecd573f35f5d509d78a188604001516040516113a29190613c88565b60405180910390a45050505050919050565b6113bc610baf565b6113d85760405162461bcd60e51b815260040161043290613ea1565b6001600160a01b0381166113fe5760405162461bcd60e51b815260040161043290613f41565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f865dab7821134b6eb27cba259b40e33bbc1b898e970a535a18a83147f380a51f90600090a250565b60405160200161146f90613c7d565b6040516020818303038152906040528051906020012081565b6004546001600160a01b031681565b60405160200161146f90613c72565b6001546000906001600160a01b031633146114d35760405162461bcd60e51b815260040161043290613e31565b6114db612985565b6114e4866106b0565b60055460c0820151602001516040517fc0b05da00000000000000000000000000000000000000000000000000000000081529293506000926001600160a01b039092169163c0b05da09161153a91600401613c88565b60206040518083038186803b15801561155257600080fd5b505afa158015611566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061158a9190810190612f61565b60ff16146115aa5760405162461bcd60e51b815260040161043290613d81565b6001600160a01b0384166000908152600760205260408120546115d490859063ffffffff611bbf16565b90506000806115e5848989866122d9565b915091506115f789858a858786611e0a565b6001600160a01b03881660009081526007602052604090205461092c90839063ffffffff611f2b16565b6002602081815260009283526040928390208054600182015493820154600383015460048401546005850154885160c081018a52600687015460a08201908152815260078701546001600160a01b0390811698820198909852600887015499810199909952600986015460608a0152600a90950154608089015292851696958516959190941693929087565b606060038054806020026020016040519081016040528092919081815260200182805480156116fb57602002820191906000526020600020905b8154815260200190600101908083116116e7575b5050505050905090565b600061170f610baf565b61172b5760405162461bcd60e51b815260040161043290613ea1565b600354821061174c5760405162461bcd60e51b815260040161043290613de1565b826003838154811061175a57fe5b9060005260206000200154146117825760405162461bcd60e51b815260040161043290613ed1565b61178a612985565b600260008581526020019081526020016000206040518060e00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600382015481526020016004820154815260200160058201548152602001600682016040518060a00160405290816000820160405180602001604052908160008201548152505081526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600282015481526020016003820154815260200160048201548152505081525050905060026000858152602001908152602001600020600080820160006101000a8154906001600160a01b0302191690556001820160006101000a8154906001600160a01b0302191690556002820160006101000a8154906001600160a01b03021916905560038201600090556004820160009055600582016000905560068201600080820160008082016000905550506001820160006101000a8154906001600160a01b0302191690556002820160009055600382016000905560048201600090555050505060036119b360016003805490506123eb90919063ffffffff16565b815481106119bd57fe5b9060005260206000200154600384815481106119d557fe5b60009182526020909120015560038054806119ec57fe5b600190038181906000526020600020016000905590556001915080602001516001600160a01b031681600001516001600160a01b0316857fadbbec6c203cb0248e89fe6d15ad651061a9d5203a1ab0273256e2b7decffa898460400151604051611a569190613c88565b60405180910390a45092915050565b6008602052600090815260409020546001600160a01b031681565b611a88610baf565b611aa45760405162461bcd60e51b815260040161043290613ea1565b611aad8161242d565b50565b6005546001600160a01b031681565b6000611ac9612985565b611ad2866106b0565b6001600160a01b03851660009081526007602052604081205491925090611b0090859063ffffffff611bbf16565b90506000611b10838888856122d9565b506001600160a01b038816600090815260076020526040902054909150610c5790829063ffffffff611f2b16565b60005460ff1615611b615760405162461bcd60e51b815260040161043290613da1565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611b953361242d565b611b9e846113b4565b611ba783610c63565b611bb082610a65565b611bb98161040e565b50505050565b600082611bce57506000611bfc565b82820282848281611bdb57fe5b0414611bf95760405162461bcd60e51b815260040161043290613e81565b90505b92915050565b60008084848780600001516001600160a01b0316836001600160a01b0316148015611c42575080602001516001600160a01b0316826001600160a01b0316145b80611c79575080602001516001600160a01b0316836001600160a01b0316148015611c79575080516001600160a01b038381169116145b611c955760405162461bcd60e51b815260040161043290613f31565b611c9e896124d0565b8151919a5094506001600160a01b0389811691161415611d5d5788604001516001600160a01b03166352707d8c8a606001518b60800151611ce68d60c0015160000151612508565b8a6040518563ffffffff1660e01b8152600401611d069493929190613f8b565b60206040518083038186803b158015611d1e57600080fd5b505afa158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d569190810190612f13565b9450611dfe565b88604001516001600160a01b03166352707d8c8a608001518b60600151611d8b8d60c0015160000151612508565b8a6040518563ffffffff1660e01b8152600401611dab9493929190613f8b565b60206040518083038186803b158015611dc357600080fd5b505afa158015611dd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611dfb9190810190612f13565b94505b50505094509492505050565b8015611e6e57426002600088815260200190815260200160002060050181905550857f949f5db193cbfa01f2d443b1c656bfede15497de0d86838cea089bd60c43834286606001518760800151604051611e65929190613f70565b60405180910390a25b611e778561250c565b15611eff5784516001600160a01b0385811691161415611eca576060850151611ea6908463ffffffff61254316565b60608601526080850151611ec0908363ffffffff6123eb16565b6080860152611eff565b6060850151611edf908363ffffffff6123eb16565b60608601526080850151611ef9908463ffffffff61254316565b60808601525b505050606082015160009384526002602052604090932060038101939093555060800151600490910155565b6000611bf983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612568565b3390565b600081604001516001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b158015611fb057600080fd5b505afa158015611fc4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611fec9190810190612ea0565b604051602001611ffc9190613c42565b604051602081830303815290604052805190602001209050919050565b6004805482516040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390921692634f8e6e2392612062929101613c88565b60206040518083038186803b15801561207a57600080fd5b505afa15801561208e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120b29190810190612d87565b6120ce5760405162461bcd60e51b815260040161043290613d91565b6004805460208301516040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390921692634f8e6e239261211a929101613c88565b60206040518083038186803b15801561213257600080fd5b505afa158015612146573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061216a9190810190612d87565b8061220c57506004805460208301516040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169263cae182fe926121bc929101613c88565b60206040518083038186803b1580156121d457600080fd5b505afa1580156121e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061220c9190810190612d87565b6122285760405162461bcd60e51b815260040161043290613e21565b60c08101515161223f9061223a6125a1565b6125c5565b61225b5760405162461bcd60e51b815260040161043290613e61565b60c0810151602001516001600160a01b0316611aad5760405162461bcd60e51b815260040161043290613ee1565b6000808260c001516080015191506000806122ab8560c00151602001516125cd565b90925090506122d0826122c4838763ffffffff611bbf16565b9063ffffffff611f2b16565b92505050915091565b60008084848780600001516001600160a01b0316836001600160a01b0316148015612319575080602001516001600160a01b0316826001600160a01b0316145b80612350575080602001516001600160a01b0316836001600160a01b0316148015612350575080516001600160a01b038381169116145b61236c5760405162461bcd60e51b815260040161043290613f31565b612375896124d0565b8151919a5094506001600160a01b03898116911614156123bd5788604001516001600160a01b031663571fd0128a606001518b60800151611ce68d60c0015160000151612508565b88604001516001600160a01b031663571fd0128a608001518b60600151611d8b8d60c0015160000151612508565b6000611bf983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061268c565b6001600160a01b0381166124535760405162461bcd60e51b815260040161043290613db1565b600080546040516001600160a01b038085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b6124d8612985565b60006124e3836126b8565b156124ff576124f183612289565b608085015260608401525060015b8291505b915091565b5190565b600060405160200161251d90613c72565b6040516020818303038152906040528051906020012061253c83611f71565b1492915050565b600082820183811015611bf95760405162461bcd60e51b815260040161043290613df1565b600081836125895760405162461bcd60e51b81526004016104329190613d70565b50600083858161259557fe5b049150505b9392505050565b6125a96129e2565b50604080516020810190915269d3c21bcecceda1000000815290565b519051111590565b6006546040517fef90e1b000000000000000000000000000000000000000000000000000000000815260009182916001600160a01b039091169063ef90e1b09061261b908690600401613c88565b604080518083038186803b15801561263257600080fd5b505afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061266a9190810190612f31565b9092509050806125035760405162461bcd60e51b815260040161043290613e41565b600081848411156126b05760405162461bcd60e51b81526004016104329190613d70565b505050900390565b6000806126c483612729565b90506126cf8361295c565b156126f157806126f15760405162461bcd60e51b815260040161043290613dc1565b60006127128460c00151604001518560a0015161254390919063ffffffff16565b42101590508080156127215750815b949350505050565b60065460c0820151602001516040517fffe736bf00000000000000000000000000000000000000000000000000000000815260009283926001600160a01b039091169163ffe736bf9161277e91600401613c88565b604080518083038186803b15801561279557600080fd5b505afa1580156127a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cd9190810190612da5565b5060c084015160608101516006546020909201516040517fbbc66a9400000000000000000000000000000000000000000000000000000000815293945060009391926001600160a01b03169163bbc66a949161282b91600401613c88565b60206040518083038186803b15801561284357600080fd5b505afa158015612857573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061287b9190810190612f13565b10159050600061289c8560c0015160400151426123eb90919063ffffffff16565b60065460c0870151602001516040517f071b48fc0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163071b48fc916128ec91600401613c88565b60206040518083038186803b15801561290457600080fd5b505afa158015612918573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061293c9190810190612f13565b1190508215801561294a5750815b80156129535750805b95945050505050565b600060405160200161251d90613c7d565b60408051808201909152600081526060602082015290565b6040518060e0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016129dd6129f5565b905290565b6040518060200160405280600081525090565b6040518060a00160405280612a086129e2565b815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8035611bfc8161409f565b8051611bfc8161409f565b60008083601f840112612a5b57600080fd5b50813567ffffffffffffffff811115612a7357600080fd5b602083019150836020820283011115612a8b57600080fd5b9250929050565b8051611bfc816140b3565b8035611bfc816140bc565b8035611bfc816140c5565b600082601f830112612ac457600080fd5b8151612ad7612ad282613fe7565b613fc0565b91508082526020830160208301858383011115612af357600080fd5b612afe838284614069565b50505092915050565b600060208284031215612b1957600080fd5b612b236020613fc0565b90506000612b318484612a9d565b82525092915050565b600060a08284031215612b4c57600080fd5b612b5660a0613fc0565b90506000612b648484612b07565b8252506020612b7584848301612a33565b6020830152506040612b8984828501612a9d565b6040830152506060612b9d84828501612a9d565b6060830152506080612bb184828501612a9d565b60808301525092915050565b60006101608284031215612bd057600080fd5b50919050565b60006101608284031215612be957600080fd5b612bf360e0613fc0565b90506000612c018484612a33565b8252506020612c1284848301612a33565b6020830152506040612c2684828501612aa8565b6040830152506060612c3a84828501612a9d565b6060830152506080612c4e84828501612a9d565b60808301525060a0612c6284828501612a9d565b60a08301525060c0612c7684828501612b3a565b60c08301525092915050565b8051611bfc816140bc565b8051611bfc816140ce565b600060208284031215612caa57600080fd5b60006127218484612a33565b60008060008060808587031215612ccc57600080fd5b6000612cd88787612a33565b9450506020612ce987828801612aa8565b9350506040612cfa87828801612aa8565b9250506060612d0b87828801612aa8565b91505092959194509250565b60008060008060408587031215612d2d57600080fd5b843567ffffffffffffffff811115612d4457600080fd5b612d5087828801612a49565b9450945050602085013567ffffffffffffffff811115612d6f57600080fd5b612d7b87828801612a49565b95989497509550505050565b600060208284031215612d9957600080fd5b60006127218484612a92565b60008060408385031215612db857600080fd5b6000612dc48585612a92565b9250506020612dd585828601612a3e565b9150509250929050565b600060208284031215612df157600080fd5b60006127218484612a9d565b60008060008060808587031215612e1357600080fd5b6000612e1f8787612a9d565b9450506020612e3087828801612a33565b9350506040612e4187828801612a33565b9250506060612d0b87828801612a9d565b60008060408385031215612e6557600080fd5b6000612e718585612a9d565b9250506020612dd585828601612a9d565b600060208284031215612e9457600080fd5b60006127218484612aa8565b600060208284031215612eb257600080fd5b815167ffffffffffffffff811115612ec957600080fd5b61272184828501612ab3565b60006101608284031215612ee857600080fd5b60006127218484612bbd565b60006101608284031215612f0757600080fd5b60006127218484612bd6565b600060208284031215612f2557600080fd5b60006127218484612c82565b60008060408385031215612f4457600080fd5b6000612f508585612c82565b9250506020612dd585828601612c82565b600060208284031215612f7357600080fd5b60006127218484612c8d565b6000612f8b8383612fab565b505060200190565b6000612f8b8383613177565b6000611bf98383613b24565b612fb481614030565b82525050565b6000612fc68385614018565b9350612fd18261400f565b8060005b8581101561300757612fe78284614021565b612ff18882612f7f565b9750612ffc83614012565b925050600101612fd5565b509495945050505050565b600061301d82612508565b6130278185614018565b935061303283614012565b8060005b8381101561300757815161304a8882612f7f565b975061305583614012565b925050600101613036565b600061306c8385614018565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561309b57600080fd5b6020830292506130ac83858461405d565b50500190565b60006130bd82612508565b6130c78185614018565b93506130d283614012565b8060005b838110156130075781516130ea8882612f93565b97506130f583614012565b9250506001016130d6565b600061310b82612508565b6131158185614018565b93508360208202850161312785614012565b8060005b8581101561316157848403895281516131448582612f9f565b945061314f83614012565b60209a909a019992505060010161312b565b5091979650505050505050565b612fb48161403b565b612fb48161400f565b612fb481614040565b600061319482612508565b61319e8185614018565b93506131ae818560208601614069565b6131b781614095565b9093019392505050565b60006131cc82612508565b6131d6818561078e565b93506131e6818560208601614069565b9290920192915050565b60006131fd602c83614018565b7f54726164696e672069732073757370656e64656420666f72207468697320726581527f666572656e636520726174650000000000000000000000000000000000000000602082015260400192915050565b600061325c603383614018565b7f617373657430206d757374206265206120737461626c6520726567697374657281527f6564207769746820746865207265736572766500000000000000000000000000602082015260400192915050565b60006132bb601c83614018565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b60006132f4602683614018565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613353600f83614018565b7f6e6f2076616c6964206d656469616e0000000000000000000000000000000000815260200192915050565b600061338c601283614018565b7f617373657430206d757374206265207365740000000000000000000000000000815260200192915050565b60006133c5601c83614018565b7f65786368616e67654964496e646578206e6f7420696e2072616e676500000000815260200192915050565b60006133fe601b83614018565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000613437600f8361078e565b7f436f6e7374616e7450726f6475637400000000000000000000000000000000008152600f0192915050565b6000613470603983614018565b7f416e2065786368616e676520776974682074686520737065636966696564206181527f737365747320616e642065786368616e67652065786973747300000000000000602082015260400192915050565b60006134cf601d83614018565b7f61737365743020646563696d616c73206d757374206265203c3d203138000000815260200192915050565b6000613508602583614018565b7f617373657431206d757374206265206120737461626c65206f7220636f6c6c6181527f746572616c000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613567601883614018565b7f43616c6c6572206973206e6f74207468652042726f6b65720000000000000000815260200192915050565b60006135a0603083614018565b7f65786368616e676520726174652064656e6f6d696e61746f72206d757374206281527f652067726561746572207468616e203000000000000000000000000000000000602082015260400192915050565b60006135ff601d83614018565b7f61737365743120646563696d616c73206d757374206265203c3d203138000000815260200192915050565b6000613638602683614018565b7f737072656164206d757374206265206c657373207468616e206f72206571756181527f6c20746f20310000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613697601983614018565b7f70726963696e674d6f64756c65206d7573742062652073657400000000000000815260200192915050565b60006136d0602183614018565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061372f601583614018565b7f696e76616c69642070726963696e674d6f64756c650000000000000000000000815260200192915050565b6000613768602083614018565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b60006137a1602283614018565b7f65786368616e6765206173736574732063616e2774206265206964656e74696381527f616c000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000613800601283614018565b7f617373657431206d757374206265207365740000000000000000000000000000815260200192915050565b6000613839600b8361078e565b7f436f6e7374616e7453756d0000000000000000000000000000000000000000008152600b0192915050565b6000613872602183614018565b7f65786368616e6765496420617420696e64657820646f65736e2774206d61746381527f6800000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006138d1601f83614018565b7f7265666572656e636552617465466565644944206d7573742062652073657400815260200192915050565b600061390a603083614018565b7f416e2065786368616e676520776974682074686520737065636966696564206981527f6420646f6573206e6f7420657869737400000000000000000000000000000000602082015260400192915050565b6000613969602183614018565b7f536f727465644f7261636c65732061646472657373206d75737420626520736581527f7400000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006139c8601b83614018565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000613a01601e83614018565b7f427265616b6572426f782061646472657373206d757374206265207365740000815260200192915050565b6000613a3a602883614018565b7f746f6b656e496e20616e6420746f6b656e4f7574206d757374206d617463682081527f65786368616e6765000000000000000000000000000000000000000000000000602082015260400192915050565b6000613a99601a83614018565b7f42726f6b65722061646472657373206d75737420626520736574000000000000815260200192915050565b6000613ad2602f83614018565b7f6964656e7469666965727320616e64206d6f64756c6573206d7573742062652081527f7468652073616d65206c656e6774680000000000000000000000000000000000602082015260400192915050565b80516000906040840190613b388582613177565b50602083015184820360208601526129538282613012565b80516020830190611bb98482613177565b805160a0830190613b728482613b50565b506020820151613b856020850182612fab565b506040820151613b986040850182613177565b506060820151613bab6060850182613177565b506080820151611bb96080850182613177565b8051610160830190613bd08482612fab565b506020820151613be36020850182612fab565b506040820151613bf66040850182613180565b506060820151613c096060850182613177565b506080820151613c1c6080850182613177565b5060a0820151613c2f60a0850182613177565b5060c0820151611bb960c0850182613b61565b600061259a82846131c1565b6000613c5a82866131c1565b9150613c6682856131c1565b915061295382846131c1565b6000611bfc8261342a565b6000611bfc8261382c565b60208101611bfc8284612fab565b6101608101613ca5828a612fab565b613cb26020830189612fab565b613cbf6040830188613180565b613ccc6060830187613177565b613cd96080830186613177565b613ce660a0830185613177565b610c5760c0830184613b61565b60408082528101613d05818688613060565b90508181036020830152613d1a818486612fba565b9695505050505050565b60208082528101611bf981846130b2565b60208082528101611bf98184613100565b60208101611bfc828461316e565b60208101611bfc8284613177565b60208101611bfc8284613180565b60208082528101611bf98184613189565b60208082528101611bfc816131f0565b60208082528101611bfc8161324f565b60208082528101611bfc816132ae565b60208082528101611bfc816132e7565b60208082528101611bfc81613346565b60208082528101611bfc8161337f565b60208082528101611bfc816133b8565b60208082528101611bfc816133f1565b60208082528101611bfc81613463565b60208082528101611bfc816134c2565b60208082528101611bfc816134fb565b60208082528101611bfc8161355a565b60208082528101611bfc81613593565b60208082528101611bfc816135f2565b60208082528101611bfc8161362b565b60208082528101611bfc8161368a565b60208082528101611bfc816136c3565b60208082528101611bfc81613722565b60208082528101611bfc8161375b565b60208082528101611bfc81613794565b60208082528101611bfc816137f3565b60208082528101611bfc81613865565b60208082528101611bfc816138c4565b60208082528101611bfc816138fd565b60208082528101611bfc8161395c565b60208082528101611bfc816139bb565b60208082528101611bfc816139f4565b60208082528101611bfc81613a2d565b60208082528101611bfc81613a8c565b60208082528101611bfc81613ac5565b6101608101611bfc8284613bbe565b60408101613f7e8285613177565b61259a6020830184613177565b60808101613f998287613177565b613fa66020830186613177565b613fb36040830185613177565b6129536060830184613177565b60405181810167ffffffffffffffff81118282101715613fdf57600080fd5b604052919050565b600067ffffffffffffffff821115613ffe57600080fd5b506020601f91909101601f19160190565b90565b60200190565b90815260200190565b6000611bf96020840184612a33565b6000611bfc8261404b565b151590565b6000611bfc82614030565b6001600160a01b031690565b60ff1690565b82818337506000910152565b60005b8381101561408457818101518382015260200161406c565b83811115611bb95750506000910152565b601f01601f191690565b6140a881614030565b8114611aad57600080fd5b6140a88161403b565b6140a88161400f565b6140a881614040565b6140a88161405756fea365627a7a7231582051ffc7b04e35ba593e770fa68c52682f6ce8a1563a825d27e055b8f924b7dca26c6578706572696d656e74616cf564736f6c63430005110040

External libraries

AddressLinkedList : 0x6200f54d73491d56b8d7a975c9ee18efb4d518df  
AddressSortedLinkedListWithMedian : 0xed477a99035d0c1e11369f1d7a4e587893cc002b