Address Details
contract

0x03C0D15A5bFD42269218A53a2451936B25DBBfAD

Contract Name
Broker
Creator
0x278160–50fc38 at 0x90b5db–b90bc5
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
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
15261762
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
Broker




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




Optimization runs
10000
EVM Version
istanbul




Verified at
2023-06-25T08:18:49.426727Z

lib/mento-core/contracts/Broker.sol

pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

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

import { IExchangeProvider } from "./interfaces/IExchangeProvider.sol";
import { IBroker } from "./interfaces/IBroker.sol";
import { IBrokerAdmin } from "./interfaces/IBrokerAdmin.sol";
import { IReserve } from "./interfaces/IReserve.sol";
import { IStableToken } from "./interfaces/IStableToken.sol";
import { IERC20Metadata } from "./common/interfaces/IERC20Metadata.sol";

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

/**
 * @title Broker
 * @notice The broker executes swaps and keeps track of spending limits per pair.
 */
contract Broker is IBroker, IBrokerAdmin, Initializable, Ownable {
  using TradingLimits for TradingLimits.State;
  using TradingLimits for TradingLimits.Config;

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

  address[] public exchangeProviders;
  mapping(address => bool) public isExchangeProvider;
  mapping(bytes32 => TradingLimits.State) public tradingLimitsState;
  mapping(bytes32 => TradingLimits.Config) public tradingLimitsConfig;

  // Address of the reserve.
  IReserve public reserve;

  uint256 private constant MAX_INT256 = uint256(-1) / 2;

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

  /**
   * @notice Sets initialized == true on implementation contracts.
   * @param test Set to true to skip implementation initialization.
   */
  constructor(bool test) public Initializable(test) {}

  /**
   * @notice Allows the contract to be upgradable via the proxy.
   * @param _exchangeProviders The addresses of the ExchangeProvider contracts.
   * @param _reserve The address of the Reserve contract.
   */
  function initialize(address[] calldata _exchangeProviders, address _reserve) external initializer {
    _transferOwnership(msg.sender);
    for (uint256 i = 0; i < _exchangeProviders.length; i++) {
      addExchangeProvider(_exchangeProviders[i]);
    }
    setReserve(_reserve);
  }

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

  /**
   * @notice Add an exchange provider to the list of providers.
   * @param exchangeProvider The address of the exchange provider to add.
   * @return index The index of the newly added specified exchange provider.
   */
  function addExchangeProvider(address exchangeProvider) public onlyOwner returns (uint256 index) {
    require(!isExchangeProvider[exchangeProvider], "ExchangeProvider already exists in the list");
    require(exchangeProvider != address(0), "ExchangeProvider address can't be 0");
    exchangeProviders.push(exchangeProvider);
    isExchangeProvider[exchangeProvider] = true;
    emit ExchangeProviderAdded(exchangeProvider);
    index = exchangeProviders.length - 1;
  }

  /**
   * @notice Remove an exchange provider from the list of providers.
   * @param exchangeProvider The address of the exchange provider to remove.
   * @param index The index of the exchange provider being removed.
   */
  function removeExchangeProvider(address exchangeProvider, uint256 index) public onlyOwner {
    require(exchangeProviders[index] == exchangeProvider, "index doesn't match provider");
    exchangeProviders[index] = exchangeProviders[exchangeProviders.length - 1];
    exchangeProviders.pop();
    delete isExchangeProvider[exchangeProvider];
    emit ExchangeProviderRemoved(exchangeProvider);
  }

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

  /**
   * @notice Calculate the amount of tokenIn to be sold for a given amountOut of tokenOut
   * @param exchangeProvider the address of the exchange manager for the pair
   * @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(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external view returns (uint256 amountIn) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    amountIn = IExchangeProvider(exchangeProvider).getAmountIn(exchangeId, tokenIn, tokenOut, amountOut);
  }

  /**
   * @notice Calculate the amount of tokenOut to be bought for a given amount of tokenIn to be sold
   * @param exchangeProvider the address of the exchange manager for the pair
   * @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(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external view returns (uint256 amountOut) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    amountOut = IExchangeProvider(exchangeProvider).getAmountOut(exchangeId, tokenIn, tokenOut, amountIn);
  }

  /**
   * @notice Execute a token swap with fixed amountIn.
   * @param exchangeProvider the address of the exchange provider for the pair.
   * @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.
   * @param amountOutMin Minimum amountOut to be received - controls slippage.
   * @return amountOut The amount of tokenOut to be bought.
   */
  function swapIn(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOutMin
  ) external returns (uint256 amountOut) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    amountOut = IExchangeProvider(exchangeProvider).swapIn(exchangeId, tokenIn, tokenOut, amountIn);
    require(amountOut >= amountOutMin, "amountOutMin not met");
    guardTradingLimits(exchangeId, tokenIn, amountIn, tokenOut, amountOut);
    transferIn(msg.sender, tokenIn, amountIn);
    transferOut(msg.sender, tokenOut, amountOut);
    emit Swap(exchangeProvider, exchangeId, msg.sender, tokenIn, tokenOut, amountIn, amountOut);
  }

  /**
   * @notice Execute a token swap with fixed amountOut.
   * @param exchangeProvider the address of the exchange provider for the pair.
   * @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.
   * @param amountInMax Maximum amount of tokenIn that can be traded.
   * @return amountIn The amount of tokenIn to be sold.
   */
  function swapOut(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut,
    uint256 amountInMax
  ) external returns (uint256 amountIn) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    amountIn = IExchangeProvider(exchangeProvider).swapOut(exchangeId, tokenIn, tokenOut, amountOut);
    require(amountIn <= amountInMax, "amountInMax exceeded");
    guardTradingLimits(exchangeId, tokenIn, amountIn, tokenOut, amountOut);
    transferIn(msg.sender, tokenIn, amountIn);
    transferOut(msg.sender, tokenOut, amountOut);
    emit Swap(exchangeProvider, exchangeId, msg.sender, tokenIn, tokenOut, amountIn, amountOut);
  }

  /**
   * @notice Permissionless way to burn stables from msg.sender directly.
   * @param token The token getting burned.
   * @param amount The amount of the token getting burned.
   * @return True if transaction succeeds.
   */
  function burnStableTokens(address token, uint256 amount) public returns (bool) {
    require(reserve.isStableAsset(token), "Token must be a reserve stable asset");
    IERC20Metadata(token).transferFrom(msg.sender, address(this), amount);
    IStableToken(token).burn(amount);
    return true;
  }

  /**
   * @notice Configure trading limits for an (exchangeId, token) touple.
   * @dev Will revert if the configuration is not valid according to the
   * TradingLimits library.
   * Resets existing state according to the TradingLimits library logic.
   * Can only be called by owner.
   * @param exchangeId the exchangeId to target.
   * @param token the token to target.
   * @param config the new trading limits config.
   */
  function configureTradingLimit(
    bytes32 exchangeId,
    address token,
    TradingLimits.Config memory config
  ) public onlyOwner {
    config.validate();

    bytes32 limitId = exchangeId ^ bytes32(uint256(uint160(token)));
    tradingLimitsConfig[limitId] = config;
    tradingLimitsState[limitId] = tradingLimitsState[limitId].reset(config);
  }

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

  /**
   * @notice Transfer a specified Mento asset to the given address.
   * If the specified asset is a stable asset it will be minted directly to the address. If
   * the asset is a collateral asset it will be transferred from the reserve to the given address.
   * @param to The address receiving the asset.
   * @param token The asset to transfer.
   * @param amount The amount of `token` to be transferred.
   */
  function transferOut(
    address payable to,
    address token,
    uint256 amount
  ) internal {
    if (reserve.isStableAsset(token)) {
      IStableToken(token).mint(to, amount);
    } else if (reserve.isCollateralAsset(token)) {
      reserve.transferExchangeCollateralAsset(token, to, amount);
    } else {
      revert("Token must be stable or collateral assert");
    }
  }

  /**
   * @notice Transfer a specified Mento asset into the reserve or the broker.
   * If the specified asset is a stable asset it will be transfered to the broker
   * and burned. If the asset is a collateral asset it will be transferred to the reserve.
   * @param from The address to transfer the asset from.
   * @param token The asset to transfer.
   * @param amount The amount of `token` to be transferred.
   */
  function transferIn(
    address payable from,
    address token,
    uint256 amount
  ) internal {
    if (reserve.isStableAsset(token)) {
      IERC20Metadata(token).transferFrom(from, address(this), amount);
      IStableToken(token).burn(amount);
    } else if (reserve.isCollateralAsset(token)) {
      IERC20Metadata(token).transferFrom(from, address(reserve), amount);
    } else {
      revert("Token must be stable or collateral assert");
    }
  }

  /**
   * @notice Verify trading limits for a trade in both directions.
   * @dev Reverts if the trading limits are met for outflow or inflow.
   * @param exchangeId the ID of the exchange being used.
   * @param _tokenIn the address of the token flowing in.
   * @param amountIn the amount of token flowing in.
   * @param _tokenOut the address of the token flowing out.
   * @param amountOut  the amount of token flowing out.
   */
  function guardTradingLimits(
    bytes32 exchangeId,
    address _tokenIn,
    uint256 amountIn,
    address _tokenOut,
    uint256 amountOut
  ) internal {
    bytes32 tokenIn = bytes32(uint256(uint160(_tokenIn)));
    bytes32 tokenOut = bytes32(uint256(uint160(_tokenOut)));
    require(amountIn <= uint256(MAX_INT256), "amountIn too large");
    require(amountOut <= uint256(MAX_INT256), "amountOut too large");

    guardTradingLimit(exchangeId ^ tokenIn, int256(amountIn), _tokenIn);
    guardTradingLimit(exchangeId ^ tokenOut, -1 * int256(amountOut), _tokenOut);
  }

  /**
   * @notice Updates and verifies a trading limit if it's configured.
   * @dev Will revert if the trading limit is exceeded by this trade.
   * @param tradingLimitId the ID of the trading limit associated with the token
   * @param deltaFlow the deltaflow of this token, negative for outflow, positive for inflow.
   * @param token the address of the token, used to lookup decimals.
   */
  function guardTradingLimit(
    bytes32 tradingLimitId,
    int256 deltaFlow,
    address token
  ) internal {
    TradingLimits.Config memory tradingLimitConfig = tradingLimitsConfig[tradingLimitId];
    if (tradingLimitConfig.flags > 0) {
      TradingLimits.State memory tradingLimitState = tradingLimitsState[tradingLimitId];
      tradingLimitState = tradingLimitState.update(tradingLimitConfig, deltaFlow, IERC20Metadata(token).decimals());
      tradingLimitState.verify(tradingLimitConfig);
      tradingLimitsState[tradingLimitId] = tradingLimitState;
    }
  }

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

  /**
   * @notice Get the list of registered exchange providers.
   * @dev This can be used by UI or clients to discover all pairs.
   * @return exchangeProviders the addresses of all exchange providers.
   */
  function getExchangeProviders() external view returns (address[] memory) {
    return exchangeProviders;
  }
}
        

/lib/mento-core/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/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/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/contracts/interfaces/IStableToken.sol

pragma solidity ^0.5.13;

/**
 * @title This interface describes the functions specific to Celo Stable Tokens, and in the
 * absence of interface inheritance is intended as a companion to IERC20.sol and ICeloToken.sol.
 */
interface IStableToken {
  function mint(address, uint256) external returns (bool);

  function burn(uint256) external returns (bool);

  function setInflationParameters(uint256, uint256) external;

  function valueToUnits(uint256) external view returns (uint256);

  function unitsToValue(uint256) external view returns (uint256);

  function getInflationParameters()
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256
    );

  function getExchangeRegistryId() external view returns (bytes32);

  // NOTE: duplicated with IERC20.sol, remove once interface inheritance is supported.
  function balanceOf(address) external view returns (uint256);
}
          

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

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/contracts/interfaces/IBrokerAdmin.sol

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

/*
 * @title Broker Admin Interface
 * @notice Contains admin functions to configure the broker that
 *         should be only callable by the owner.
 */
interface IBrokerAdmin {
  /**
   * @notice Emitted when an ExchangeProvider is added.
   * @param exchangeProvider The address of the ExchangeProvider.
   */
  event ExchangeProviderAdded(address indexed exchangeProvider);

  /**
   * @notice Emitted when an ExchangeProvider is removed.
   * @param exchangeProvider The address of the ExchangeProvider.
   */
  event ExchangeProviderRemoved(address indexed exchangeProvider);

  /**
   * @notice Emitted when the reserve is updated.
   * @param newAddress The new address.
   * @param prevAddress The previous address.
   */
  event ReserveSet(address indexed newAddress, address indexed prevAddress);

  /**
   * @notice Remove an ExchangeProvider at a specified index.
   * @param exchangeProvider The address of the ExchangeProvider to remove.
   * @param index The index in the exchange providers array.
   */
  function removeExchangeProvider(address exchangeProvider, uint256 index) external;

  /**
   * @notice Add an ExchangeProvider.
   * @param exchangeProvider The address of the ExchangeProvider to add.
   * @return index The index where the ExchangeProvider was inserted.
   */
  function addExchangeProvider(address exchangeProvider) external returns (uint256 index);

  /**
   * @notice Set the Mento reserve address.
   * @param reserve The Mento reserve address.
   */
  function setReserve(address reserve) external;
}
          

/lib/mento-core/contracts/interfaces/IBroker.sol

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

/*
 * @title Broker Interface for trader functions
 * @notice The broker is responsible for executing swaps and keeping track of trading limits.
 */
interface IBroker {
  /**
   * @notice Emitted when a swap occurs.
   * @param exchangeProvider The exchange provider used.
   * @param exchangeId The id of the exchange used.
   * @param trader The user that initiated the swap.
   * @param tokenIn The address of the token that was sold.
   * @param tokenOut The address of the token that was bought.
   * @param amountIn The amount of token sold.
   * @param amountOut The amount of token bought.
   */
  event Swap(
    address exchangeProvider,
    bytes32 indexed exchangeId,
    address indexed trader,
    address indexed tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOut
  );

  /**
   * @notice Execute a token swap with fixed amountIn.
   * @param exchangeProvider the address of the exchange provider for the pair.
   * @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.
   * @param amountOutMin Minimum amountOut to be received - controls slippage.
   * @return amountOut The amount of tokenOut to be bought.
   */
  function swapIn(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOutMin
  ) external returns (uint256 amountOut);

  /**
   * @notice Execute a token swap with fixed amountOut.
   * @param exchangeProvider the address of the exchange provider for the pair.
   * @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.
   * @param amountInMax Maximum amount of tokenIn that can be traded.
   * @return amountIn The amount of tokenIn to be sold.
   */
  function swapOut(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut,
    uint256 amountInMax
  ) external returns (uint256 amountIn);

  /**
   * @notice Calculate amountOut of tokenOut received for a given amountIn of tokenIn.
   * @param exchangeProvider the address of the exchange provider for the pair.
   * @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(
    address exchangeProvider,
    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 exchangeProvider the address of the exchange provider for the pair.
   * @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(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external view returns (uint256 amountIn);

  /**
   * @notice Get the list of registered exchange providers.
   * @dev This can be used by UI or clients to discover all pairs.
   * @return exchangeProviders the addresses of all exchange providers.
   */
  function getExchangeProviders() external view returns (address[] memory exchangeProviders);
}
          

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

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/contracts/common/TradingLimits.sol

pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

/**
 * @title TradingLimits
 * @author Mento Team
 * @notice This library provides data structs and utility functions for
 * defining and verifying trading limits on the netflow of an asset.
 * There are three limits that can be enabled:
 * - L0: A timewindow based limit, verifies that:
 *       -1 * limit0 <= netflow0 <= limit0,
 *       for a netflow0 that resets every timespan0 seconds.
 * - L1: A timewindow based limit, verifies that:
 *       -1 * limit1 <= netflow1 <= limit1,
 *       for a netflow1 that resets every timespan1 second.
 * - LG: A global (or lifetime) limit that ensures that:
 *       -1 * limitGlobal <= netflowGlobal <= limitGlobal,
 *       for a netflowGlobal that doesn't reset until the
 *       limit is disabled.
 * @dev All contained functions are pure or view and marked internal to
 * be inlined on consuming contracts at compile time for gas efficiency.
 * Both State and Config structs are designed to be packed in one
 * storage slot each.
 * In order to pack both the state and config into one slot each,
 * some assumptions are made:
 * 1. limit{0,1,Global} and netflow{0,1,Global} are recorded with
 *    ZERO decimals precision to fit in an int48.
 *    Any subunit delta in netflow will be rounded up to one unit.
 * 2. netflow{0,1,Global} have to fit in int48, thus have to fit in the range:
 *    -140_737_488_355_328 to 140_737_488_355_328, which can cover most
 *    tokens of interest, but will break down for tokens which trade
 *    in large unit values.
 * 3. timespan{0,1} and lastUpdated{0,1} have to fit in int32 therefore
 *    the timestamps will overflow sometime in the year 2102.
 *
 * The library ensures that netflow0 and netflow1 are reset during
 * the update phase, but does not control how the full State gets
 * updated if the Config changes, this is left to the library consumer.
 */
library TradingLimits {
  uint8 private constant L0 = 1; // 0b001 Limit0
  uint8 private constant L1 = 2; // 0b010 Limit1
  uint8 private constant LG = 4; // 0b100 LimitGlobal
  int48 private constant MAX_INT48 = int48(uint48(-1) / 2);

  struct State {
    uint32 lastUpdated0;
    uint32 lastUpdated1;
    int48 netflow0;
    int48 netflow1;
    int48 netflowGlobal;
  }

  struct Config {
    uint32 timestep0;
    uint32 timestep1;
    int48 limit0;
    int48 limit1;
    int48 limitGlobal;
    uint8 flags;
  }

  /**
   * @notice Validate a trading limit configuration.
   * @dev Reverts if the configuration is malformed.
   * @param self the Config struct to check.
   */
  function validate(Config memory self) internal pure {
    require(self.flags & L1 == 0 || self.flags & L0 != 0, "L1 without L0 not allowed");
    require(self.flags & L0 == 0 || self.timestep0 > 0, "timestep0 can't be zero if active");
    require(self.flags & L1 == 0 || self.timestep1 > 0, "timestep1 can't be zero if active");
  }

  /**
   * @notice Verify a trading limit State with a provided Config.
   * @dev Reverts if the limits are exceeded.
   * @param self the trading limit State to check.
   * @param config the trading limit Config to check against.
   */
  function verify(State memory self, Config memory config) internal pure {
    if ((config.flags & L0) > 0 && (-1 * config.limit0 > self.netflow0 || self.netflow0 > config.limit0)) {
      revert("L0 Exceeded");
    }
    if ((config.flags & L1) > 0 && (-1 * config.limit1 > self.netflow1 || self.netflow1 > config.limit1)) {
      revert("L1 Exceeded");
    }
    if (
      (config.flags & LG) > 0 &&
      (-1 * config.limitGlobal > self.netflowGlobal || self.netflowGlobal > config.limitGlobal)
    ) {
      revert("LG Exceeded");
    }
  }

  /**
   * @notice Reset an existing state with a new config.
   * It keps netflows of enabled limits and resets when disabled.
   * It resets all timestamp checkpoints to reset time-window limits
   * on next swap.
   * @param self the trading limit state to reset.
   * @param config the updated config to reset against.
   * @return the reset state.
   */
  function reset(State memory self, Config memory config) internal pure returns (State memory) {
    // Ensure the next swap will reset the trading limits windows.
    self.lastUpdated0 = 0;
    self.lastUpdated1 = 0;
    if (config.flags & L0 == 0) {
      self.netflow0 = 0;
    }
    if (config.flags & L1 == 0) {
      self.netflow1 = 0;
    }
    if (config.flags & LG == 0) {
      self.netflowGlobal = 0;
    }
    return self;
  }

  /**
   * @notice  Updates a trading limit State in the context of a Config with the deltaFlow provided.
   * @dev Reverts if the values provided cause overflows.
   * @param self the trading limit State to update.
   * @param config the trading limit Config for the provided State.
   * @param _deltaFlow the delta flow to add to the netflow.
   * @param decimals the number of decimals the _deltaFlow is denominated in.
   * @return State the updated state.
   */
  function update(
    State memory self,
    Config memory config,
    int256 _deltaFlow,
    uint8 decimals
  ) internal view returns (State memory) {
    int256 _deltaFlowUnits = _deltaFlow / int256((10**uint256(decimals)));
    require(_deltaFlowUnits <= MAX_INT48, "dFlow too large");
    int48 deltaFlowUnits = _deltaFlowUnits == 0 ? 1 : int48(_deltaFlowUnits);

    if (config.flags & L0 > 0) {
      if (block.timestamp > self.lastUpdated0 + config.timestep0) {
        self.netflow0 = 0;
        self.lastUpdated0 = uint32(block.timestamp);
      }
      self.netflow0 = safeINT48Add(self.netflow0, deltaFlowUnits);

      if (config.flags & L1 > 0) {
        if (block.timestamp > self.lastUpdated1 + config.timestep1) {
          self.netflow1 = 0;
          self.lastUpdated1 = uint32(block.timestamp);
        }
        self.netflow1 = safeINT48Add(self.netflow1, deltaFlowUnits);
      }
    }
    if (config.flags & LG > 0) {
      self.netflowGlobal = safeINT48Add(self.netflowGlobal, deltaFlowUnits);
    }

    return self;
  }

  /**
   * @notice Safe add two int48s.
   * @dev Reverts if addition causes over/underflow.
   * @param a number to add.
   * @param b number to add.
   * @return int48 result of addition.
   */
  function safeINT48Add(int48 a, int48 b) internal pure returns (int48) {
    int256 c = int256(a) + int256(b);
    require(c >= -1 * MAX_INT48 && c <= MAX_INT48, "int48 addition overflow");
    return int48(c);
  }
}
          

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

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;
    _;
  }
}
          

Compiler Settings

{"remappings":[":celo-foundry/=lib/celo-foundry/src/",":ds-test/=lib/celo-foundry/lib/forge-std/lib/ds-test/src/",":forge-std/=lib/celo-foundry/lib/forge-std/src/",":mento-core/=lib/mento-core/",":openzeppelin-contracts/=lib/mento-core/lib/openzeppelin-contracts/contracts/",":openzeppelin-solidity/=lib/mento-core/lib/openzeppelin-contracts/"],"optimizer":{"runs":10000,"enabled":true},"libraries":{},"evmVersion":"istanbul","compilationTarget":{"lib/mento-core/contracts/Broker.sol":"Broker"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[{"type":"bool","name":"test","internalType":"bool"}]},{"type":"event","name":"ExchangeProviderAdded","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ExchangeProviderRemoved","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address","indexed":true}],"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":"ReserveSet","inputs":[{"type":"address","name":"newAddress","internalType":"address","indexed":true},{"type":"address","name":"prevAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Swap","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address","indexed":false},{"type":"bytes32","name":"exchangeId","internalType":"bytes32","indexed":true},{"type":"address","name":"trader","internalType":"address","indexed":true},{"type":"address","name":"tokenIn","internalType":"address","indexed":true},{"type":"address","name":"tokenOut","internalType":"address","indexed":false},{"type":"uint256","name":"amountIn","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOut","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":"index","internalType":"uint256"}],"name":"addExchangeProvider","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"burnStableTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"configureTradingLimit","inputs":[{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"token","internalType":"address"},{"type":"tuple","name":"config","internalType":"struct TradingLimits.Config","components":[{"type":"uint32","name":"timestep0","internalType":"uint32"},{"type":"uint32","name":"timestep1","internalType":"uint32"},{"type":"int48","name":"limit0","internalType":"int48"},{"type":"int48","name":"limit1","internalType":"int48"},{"type":"int48","name":"limitGlobal","internalType":"int48"},{"type":"uint8","name":"flags","internalType":"uint8"}]}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"exchangeProviders","inputs":[{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"},{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"},{"type":"bytes32","name":"exchangeId","internalType":"bytes32"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getExchangeProviders","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[{"type":"address[]","name":"_exchangeProviders","internalType":"address[]"},{"type":"address","name":"_reserve","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"initialized","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExchangeProvider","inputs":[{"type":"address","name":"","internalType":"address"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"removeExchangeProvider","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"},{"type":"uint256","name":"index","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"renounceOwnership","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"contract IReserve"}],"name":"reserve","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"setReserve","inputs":[{"type":"address","name":"_reserve","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapIn","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"},{"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":"uint256","name":"amountOutMin","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapOut","inputs":[{"type":"address","name":"exchangeProvider","internalType":"address"},{"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":"uint256","name":"amountInMax","internalType":"uint256"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint32","name":"timestep0","internalType":"uint32"},{"type":"uint32","name":"timestep1","internalType":"uint32"},{"type":"int48","name":"limit0","internalType":"int48"},{"type":"int48","name":"limit1","internalType":"int48"},{"type":"int48","name":"limitGlobal","internalType":"int48"},{"type":"uint8","name":"flags","internalType":"uint8"}],"name":"tradingLimitsConfig","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint32","name":"lastUpdated0","internalType":"uint32"},{"type":"uint32","name":"lastUpdated1","internalType":"uint32"},{"type":"int48","name":"netflow0","internalType":"int48"},{"type":"int48","name":"netflow1","internalType":"int48"},{"type":"int48","name":"netflowGlobal","internalType":"int48"}],"name":"tradingLimitsState","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}],"constant":false}]
              

Contract Creation Code

0x60806040523480156200001157600080fd5b506040516200309d3803806200309d8339810160408190526200003491620000cf565b808062000049576000805460ff191660011790555b5060006200005f6001600160e01b03620000b816565b60008054610100600160a81b0319166101006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3505062000117565b3390565b8051620000c981620000fd565b92915050565b600060208284031215620000e257600080fd5b6000620000f08484620000bc565b949350505050565b151590565b6200010881620000f8565b81146200011457600080fd5b50565b612f7680620001276000396000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c80638f32d59b116100d8578063cd3293de1161008c578063ddbbe85011610066578063ddbbe850146102ee578063f01ecd1714610301578063f2fde38b1461032557610177565b8063cd3293de146102b3578063d163b135146102c8578063d1d786b1146102db57610177565b8063a20f2305116100bd578063a20f23051461027a578063a9b5aab31461028d578063c4454fdc146102a057610177565b80638f32d59b1461025f5780639cecc80a1461026757610177565b8063462d0b2e1161012f57806373ec4cf41161011457806373ec4cf414610212578063821a816c146102255780638da5cb5b1461024a57610177565b8063462d0b2e146101f7578063715018a61461020a57610177565b8063131cab2a11610160578063131cab2a146101ba578063158ef93e146101da5780632cac2568146101e257610177565b806304710d531461017c57806304e4564014610191575b600080fd5b61018f61018a366004612380565b610338565b005b6101a461019f366004612284565b6104c4565b6040516101b19190612dc8565b60405180910390f35b6101cd6101c8366004612380565b6105a1565b6040516101b19190612c24565b6101cd610796565b6101ea61079f565b6040516101b19190612c13565b61018f6102053660046123ba565b610801565b61018f61089f565b6101a4610220366004612266565b610929565b61023861023336600461242d565b610a79565b6040516101b196959493929190612e18565b610252610b03565b6040516101b19190612b76565b6101cd610b17565b61018f610275366004612266565b610b40565b6101a4610288366004612284565b610bff565b61018f61029b36600461244b565b610c82565b6102526102ae36600461242d565b610ffd565b6102bb611024565b6040516101b19190612c5a565b6101a46102d63660046122f9565b611033565b6101cd6102e9366004612266565b6111a9565b6101a46102fc3660046122f9565b6111be565b61031461030f36600461242d565b611322565b6040516101b1959493929190612dd6565b61018f610333366004612266565b61138b565b610340610b17565b6103655760405162461bcd60e51b815260040161035c90612d38565b60405180910390fd5b816001600160a01b03166001828154811061037c57fe5b6000918252602090912001546001600160a01b0316146103ae5760405162461bcd60e51b815260040161035c90612cc8565b6001805460001981019081106103c057fe5b600091825260209091200154600180546001600160a01b0390921691839081106103e657fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600180548061041f57fe5b60008281526020808220830160001990810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559092019092556001600160a01b0384168083526002909152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b6001600160a01b03851660009081526002602052604081205460ff166104fc5760405162461bcd60e51b815260040161035c90612d08565b6040517ff670dde10000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063f670dde190610547908890889088908890600401612c32565b60206040518083038186803b15801561055f57600080fd5b505afa158015610573573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610597919081019061248f565b9695505050505050565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690634f8e6e23906105eb908690600401612b76565b60206040518083038186803b15801561060357600080fd5b505afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061063b919081019061240f565b6106575760405162461bcd60e51b815260040161035c90612ce8565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038416906323b872dd906106a090339030908790600401612b84565b602060405180830381600087803b1580156106ba57600080fd5b505af11580156106ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106f2919081019061240f565b506040517f42966c680000000000000000000000000000000000000000000000000000000081526001600160a01b038416906342966c6890610738908590600401612dc8565b602060405180830381600087803b15801561075257600080fd5b505af1158015610766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061078a919081019061240f565b50600190505b92915050565b60005460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156107f757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107d9575b5050505050905090565b60005460ff16156108245760405162461bcd60e51b815260040161035c90612c78565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610858336113bb565b60005b828110156108905761088784848381811061087257fe5b90506020020160206102209190810190612266565b5060010161085b565b5061089a81610b40565b505050565b6108a7610b17565b6108c35760405162461bcd60e51b815260040161035c90612d38565b600080546040516101009091046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b6000610933610b17565b61094f5760405162461bcd60e51b815260040161035c90612d38565b6001600160a01b03821660009081526002602052604090205460ff16156109885760405162461bcd60e51b815260040161035c90612c68565b6001600160a01b0382166109ae5760405162461bcd60e51b815260040161035c90612d28565b6001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03851690811790915560008181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909417909355915190917f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de291a250506001546000190190565b60046020526000908152604090205463ffffffff80821691640100000000810490911690680100000000000000008104600590810b916e0100000000000000000000000000008104820b91740100000000000000000000000000000000000000008204900b907a010000000000000000000000000000000000000000000000000000900460ff1686565b60005461010090046001600160a01b031690565b6000805461010090046001600160a01b0316610b3161145e565b6001600160a01b031614905090565b610b48610b17565b610b645760405162461bcd60e51b815260040161035c90612d38565b6001600160a01b038116610b8a5760405162461bcd60e51b815260040161035c90612da8565b6005546040516001600160a01b03918216918316907fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5090600090a3600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6001600160a01b03851660009081526002602052604081205460ff16610c375760405162461bcd60e51b815260040161035c90612d08565b6040517f93c7e3bc0000000000000000000000000000000000000000000000000000000081526001600160a01b038716906393c7e3bc90610547908890889088908890600401612c32565b610c8a610b17565b610ca65760405162461bcd60e51b815260040161035c90612d38565b610caf81611462565b6000826001600160a01b031660001b84189050816004600083815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548165ffffffffffff021916908360050b65ffffffffffff160217905550606082015181600001600e6101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060808201518160000160146101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060a082015181600001601a6101000a81548160ff021916908360ff160217905550905050610ea582600360008481526020019081526020016000206040518060a00160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160089054906101000a900460050b60050b60050b815260200160008201600e9054906101000a900460050b60050b60050b81526020016000820160149054906101000a900460050b60050b60050b8152505061150890919063ffffffff16565b60009182526003602090815260409283902082518154928401519484015160608501516080909501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090941663ffffffff928316177fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff166401000000009290961691909102949094177fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff1668010000000000000000600595860b65ffffffffffff90811691909102919091177fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000094860b821694909402939093177fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000009290940b9290921602919091179055505050565b6001818154811061100a57fe5b6000918252602090912001546001600160a01b0316905081565b6005546001600160a01b031681565b6001600160a01b03861660009081526002602052604081205460ff1661106b5760405162461bcd60e51b815260040161035c90612d08565b6040517fd3385d050000000000000000000000000000000000000000000000000000000081526001600160a01b0388169063d3385d05906110b6908990899089908990600401612c32565b602060405180830381600087803b1580156110d057600080fd5b505af11580156110e4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611108919081019061248f565b90508181111561112a5760405162461bcd60e51b815260040161035c90612ca8565b611137868683878761155d565b611142338683611612565b61114d3385856118ec565b846001600160a01b0316336001600160a01b0316877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a8886896040516111979493929190612bd5565b60405180910390a49695505050505050565b60026020526000908152604090205460ff1681565b6001600160a01b03861660009081526002602052604081205460ff166111f65760405162461bcd60e51b815260040161035c90612d08565b6040517f42bfc99c0000000000000000000000000000000000000000000000000000000081526001600160a01b038816906342bfc99c90611241908990899089908990600401612c32565b602060405180830381600087803b15801561125b57600080fd5b505af115801561126f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611293919081019061248f565b9050818110156112b55760405162461bcd60e51b815260040161035c90612d78565b6112c2868685878561155d565b6112cd338685611612565b6112d83385836118ec565b846001600160a01b0316336001600160a01b0316877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a8888876040516111979493929190612bd5565b60036020526000908152604090205463ffffffff80821691640100000000810490911690680100000000000000008104600590810b916e0100000000000000000000000000008104820b9174010000000000000000000000000000000000000000909104900b85565b611393610b17565b6113af5760405162461bcd60e51b815260040161035c90612d38565b6113b8816113bb565b50565b6001600160a01b0381166113e15760405162461bcd60e51b815260040161035c90612c88565b600080546040516001600160a01b038085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b3390565b60a0810151600216158061147c575060a081015160011615155b6114985760405162461bcd60e51b815260040161035c90612cb8565b60a081015160011615806114b25750805163ffffffff1615155b6114ce5760405162461bcd60e51b815260040161035c90612d18565b60a081015160021615806114ec57506000816020015163ffffffff16115b6113b85760405162461bcd60e51b815260040161035c90612cd8565b6115106120cb565b6000808452602084015260a082015160011661152e57600060408401525b60a082015160021661154257600060608401525b60a082015160041661155657600060808401525b5090919050565b6001600160a01b038481169083167f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8511156115ab5760405162461bcd60e51b815260040161035c90612d48565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156115eb5760405162461bcd60e51b815260040161035c90612d58565b6115f88288188688611abc565b611609818818846000190286611abc565b50505050505050565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634f8e6e239061165b908590600401612b76565b60206040518083038186803b15801561167357600080fd5b505afa158015611687573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116ab919081019061240f565b156117e9576040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038316906323b872dd906116f990869030908690600401612b84565b602060405180830381600087803b15801561171357600080fd5b505af1158015611727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061174b919081019061240f565b506040517f42966c680000000000000000000000000000000000000000000000000000000081526001600160a01b038316906342966c6890611791908490600401612dc8565b602060405180830381600087803b1580156117ab57600080fd5b505af11580156117bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e3919081019061240f565b5061089a565b6005546040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063cae182fe90611832908590600401612b76565b60206040518083038186803b15801561184a57600080fd5b505afa15801561185e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611882919081019061240f565b156118d4576005546040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03808516926323b872dd9261179192889216908690600401612b84565b60405162461bcd60e51b815260040161035c90612db8565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634f8e6e2390611935908590600401612b76565b60206040518083038186803b15801561194d57600080fd5b505afa158015611961573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611985919081019061240f565b156119d1576040517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b038316906340c10f19906117919086908590600401612bac565b6005546040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063cae182fe90611a1a908590600401612b76565b60206040518083038186803b158015611a3257600080fd5b505afa158015611a46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a6a919081019061240f565b156118d4576005546040517f6be383fc0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690636be383fc9061179190859087908690600401612bc7565b611ac46120f9565b50600083815260046020908152604091829020825160c081018452905463ffffffff808216835264010000000082041692820192909252680100000000000000008204600590810b810b810b938201939093526e0100000000000000000000000000008204830b830b830b6060820152740100000000000000000000000000000000000000008204830b830b90920b60808301527a010000000000000000000000000000000000000000000000000000900460ff1660a0820181905215611e2157611b8d6120cb565b50600084815260036020908152604091829020825160a081018452905463ffffffff808216835264010000000082041682840152680100000000000000008104600590810b810b810b838601526e0100000000000000000000000000008204810b810b810b606084015274010000000000000000000000000000000000000000909104810b810b900b608082015282517f313ce56700000000000000000000000000000000000000000000000000000000815292519092611cbe92859288926001600160a01b0389169263313ce56792600483810193829003018186803b158015611c7757600080fd5b505afa158015611c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611caf91908101906124ad565b8492919063ffffffff611e2716565b9050611cd0818363ffffffff611f5116565b60008581526003602090815260409182902083518154928501519385015160608601516080909601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090941663ffffffff928316177fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff166401000000009290951691909102939093177fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff1668010000000000000000600594850b65ffffffffffff90811691909102919091177fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000095850b821695909502949094177fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000009290930b93909316021790555b50505050565b611e2f6120cb565b60008260ff16600a0a8481611e4057fe5b059050657fffffffffff811315611e695760405162461bcd60e51b815260040161035c90612d88565b60008115611e775781611e7a565b60015b60a087015190915060011615611f1b57855187510163ffffffff16421115611ead576000604088015263ffffffff421687525b611ebb876040015182612066565b600590810b900b604088015260a086015160021615611f1b57856020015187602001510163ffffffff16421115611f00576000606088015263ffffffff421660208801525b611f0e876060015182612066565b600590810b900b60608801525b60a086015160041615611f4357611f36876080015182612066565b600590810b900b60808801525b86925050505b949350505050565b60a081015160011615801590611f8f5750816040015160050b81604001516000190260050b1380611f8f5750806040015160050b826040015160050b135b15611fac5760405162461bcd60e51b815260040161035c90612d68565b60a081015160021615801590611fea5750816060015160050b81606001516000190260050b1380611fea5750806060015160050b826060015160050b135b156120075760405162461bcd60e51b815260040161035c90612cf8565b60a0810151600416158015906120455750816080015160050b81608001516000190260050b13806120455750806080015160050b826080015160050b135b156120625760405162461bcd60e51b815260040161035c90612d98565b5050565b6000600582810b9084900b017fffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000181128015906120a85750657fffffffffff8113155b6120c45760405162461bcd60e51b815260040161035c90612c98565b9392505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b803561079081612ef2565b60008083601f84011261214b57600080fd5b50813567ffffffffffffffff81111561216357600080fd5b60208301915083602082028301111561217b57600080fd5b9250929050565b805161079081612f06565b803561079081612f0f565b803561079081612f18565b600060c082840312156121b557600080fd5b6121bf60c0612e72565b905060006121cd8484612245565b82525060206121de84848301612245565b60208301525060406121f284828501612198565b604083015250606061220684828501612198565b606083015250608061221a84828501612198565b60808301525060a061222e84828501612250565b60a08301525092915050565b805161079081612f0f565b803561079081612f21565b803561079081612f2a565b805161079081612f2a565b60006020828403121561227857600080fd5b6000611f49848461212e565b600080600080600060a0868803121561229c57600080fd5b60006122a8888861212e565b95505060206122b98882890161218d565b94505060406122ca8882890161212e565b93505060606122db8882890161212e565b92505060806122ec8882890161218d565b9150509295509295909350565b60008060008060008060c0878903121561231257600080fd5b600061231e898961212e565b965050602061232f89828a0161218d565b955050604061234089828a0161212e565b945050606061235189828a0161212e565b935050608061236289828a0161218d565b92505060a061237389828a0161218d565b9150509295509295509295565b6000806040838503121561239357600080fd5b600061239f858561212e565b92505060206123b08582860161218d565b9150509250929050565b6000806000604084860312156123cf57600080fd5b833567ffffffffffffffff8111156123e657600080fd5b6123f286828701612139565b935093505060206124058682870161212e565b9150509250925092565b60006020828403121561242157600080fd5b6000611f498484612182565b60006020828403121561243f57600080fd5b6000611f49848461218d565b6000806000610100848603121561246157600080fd5b600061246d868661218d565b935050602061247e8682870161212e565b9250506040612405868287016121a3565b6000602082840312156124a157600080fd5b6000611f49848461223a565b6000602082840312156124bf57600080fd5b6000611f49848461225b565b60006124d783836124ee565b505060200190565b6124e881612ee0565b82525050565b6124e881612eac565b600061250282612e9f565b61250c8185612ea3565b935061251783612e99565b8060005b8381101561254557815161252f88826124cb565b975061253a83612e99565b92505060010161251b565b509495945050505050565b6124e881612eb7565b6124e881612ebc565b6124e881612ee7565b6124e881612ebf565b6000612581602b83612ea3565b7f45786368616e676550726f766964657220616c7265616479206578697374732081527f696e20746865206c697374000000000000000000000000000000000000000000602082015260400192915050565b60006125e0601c83612ea3565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b6000612619602683612ea3565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000612678601783612ea3565b7f696e743438206164646974696f6e206f766572666c6f77000000000000000000815260200192915050565b60006126b1601483612ea3565b7f616d6f756e74496e4d6178206578636565646564000000000000000000000000815260200192915050565b60006126ea601983612ea3565b7f4c3120776974686f7574204c30206e6f7420616c6c6f77656400000000000000815260200192915050565b6000612723601c83612ea3565b7f696e64657820646f65736e2774206d617463682070726f766964657200000000815260200192915050565b600061275c602183612ea3565b7f74696d6573746570312063616e2774206265207a65726f20696620616374697681527f6500000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006127bb602483612ea3565b7f546f6b656e206d7573742062652061207265736572766520737461626c65206181527f7373657400000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061281a600b83612ea3565b7f4c31204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612853601f83612ea3565b7f45786368616e676550726f766964657220646f6573206e6f7420657869737400815260200192915050565b600061288c602183612ea3565b7f74696d6573746570302063616e2774206265207a65726f20696620616374697681527f6500000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006128eb602383612ea3565b7f45786368616e676550726f766964657220616464726573732063616e2774206281527f6520300000000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061294a602083612ea3565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000612983601283612ea3565b7f616d6f756e74496e20746f6f206c617267650000000000000000000000000000815260200192915050565b60006129bc601383612ea3565b7f616d6f756e744f757420746f6f206c6172676500000000000000000000000000815260200192915050565b60006129f5600b83612ea3565b7f4c30204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612a2e601483612ea3565b7f616d6f756e744f75744d696e206e6f74206d6574000000000000000000000000815260200192915050565b6000612a67600f83612ea3565b7f64466c6f7720746f6f206c617267650000000000000000000000000000000000815260200192915050565b6000612aa0600b83612ea3565b7f4c47204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612ad9601b83612ea3565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000612b12602983612ea3565b7f546f6b656e206d75737420626520737461626c65206f7220636f6c6c6174657281527f616c206173736572740000000000000000000000000000000000000000000000602082015260400192915050565b6124e881612ed1565b6124e881612eda565b6020810161079082846124ee565b60608101612b9282866124df565b612b9f60208301856124ee565b611f496040830184612559565b60408101612bba82856124df565b6120c46020830184612559565b60608101612b9282866124ee565b60808101612be382876124ee565b612bf060208301866124ee565b612bfd6040830185612559565b612c0a6060830184612559565b95945050505050565b602080825281016120c481846124f7565b602081016107908284612550565b60808101612c408287612559565b612c4d60208301866124ee565b612bfd60408301856124ee565b602081016107908284612562565b6020808252810161079081612574565b60208082528101610790816125d3565b602080825281016107908161260c565b602080825281016107908161266b565b60208082528101610790816126a4565b60208082528101610790816126dd565b6020808252810161079081612716565b602080825281016107908161274f565b60208082528101610790816127ae565b602080825281016107908161280d565b6020808252810161079081612846565b602080825281016107908161287f565b60208082528101610790816128de565b602080825281016107908161293d565b6020808252810161079081612976565b60208082528101610790816129af565b60208082528101610790816129e8565b6020808252810161079081612a21565b6020808252810161079081612a5a565b6020808252810161079081612a93565b6020808252810161079081612acc565b6020808252810161079081612b05565b602081016107908284612559565b60a08101612de48288612b64565b612df16020830187612b64565b612dfe604083018661256b565b612e0b606083018561256b565b610597608083018461256b565b60c08101612e268289612b64565b612e336020830188612b64565b612e40604083018761256b565b612e4d606083018661256b565b612e5a608083018561256b565b612e6760a0830184612b6d565b979650505050505050565b60405181810167ffffffffffffffff81118282101715612e9157600080fd5b604052919050565b60200190565b5190565b90815260200190565b600061079082612ec5565b151590565b90565b60050b90565b6001600160a01b031690565b63ffffffff1690565b60ff1690565b6000610790825b600061079082612eac565b612efb81612eac565b81146113b857600080fd5b612efb81612eb7565b612efb81612ebc565b612efb81612ebf565b612efb81612ed1565b612efb81612eda56fea365627a7a7231582067f8831f916fddf7b86c3a3cf24b9b5ecc4ad9361173def8fcd4c058c8965a616c6578706572696d656e74616cf564736f6c634300051100400000000000000000000000000000000000000000000000000000000000000000

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101775760003560e01c80638f32d59b116100d8578063cd3293de1161008c578063ddbbe85011610066578063ddbbe850146102ee578063f01ecd1714610301578063f2fde38b1461032557610177565b8063cd3293de146102b3578063d163b135146102c8578063d1d786b1146102db57610177565b8063a20f2305116100bd578063a20f23051461027a578063a9b5aab31461028d578063c4454fdc146102a057610177565b80638f32d59b1461025f5780639cecc80a1461026757610177565b8063462d0b2e1161012f57806373ec4cf41161011457806373ec4cf414610212578063821a816c146102255780638da5cb5b1461024a57610177565b8063462d0b2e146101f7578063715018a61461020a57610177565b8063131cab2a11610160578063131cab2a146101ba578063158ef93e146101da5780632cac2568146101e257610177565b806304710d531461017c57806304e4564014610191575b600080fd5b61018f61018a366004612380565b610338565b005b6101a461019f366004612284565b6104c4565b6040516101b19190612dc8565b60405180910390f35b6101cd6101c8366004612380565b6105a1565b6040516101b19190612c24565b6101cd610796565b6101ea61079f565b6040516101b19190612c13565b61018f6102053660046123ba565b610801565b61018f61089f565b6101a4610220366004612266565b610929565b61023861023336600461242d565b610a79565b6040516101b196959493929190612e18565b610252610b03565b6040516101b19190612b76565b6101cd610b17565b61018f610275366004612266565b610b40565b6101a4610288366004612284565b610bff565b61018f61029b36600461244b565b610c82565b6102526102ae36600461242d565b610ffd565b6102bb611024565b6040516101b19190612c5a565b6101a46102d63660046122f9565b611033565b6101cd6102e9366004612266565b6111a9565b6101a46102fc3660046122f9565b6111be565b61031461030f36600461242d565b611322565b6040516101b1959493929190612dd6565b61018f610333366004612266565b61138b565b610340610b17565b6103655760405162461bcd60e51b815260040161035c90612d38565b60405180910390fd5b816001600160a01b03166001828154811061037c57fe5b6000918252602090912001546001600160a01b0316146103ae5760405162461bcd60e51b815260040161035c90612cc8565b6001805460001981019081106103c057fe5b600091825260209091200154600180546001600160a01b0390921691839081106103e657fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600180548061041f57fe5b60008281526020808220830160001990810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690559092019092556001600160a01b0384168083526002909152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b6001600160a01b03851660009081526002602052604081205460ff166104fc5760405162461bcd60e51b815260040161035c90612d08565b6040517ff670dde10000000000000000000000000000000000000000000000000000000081526001600160a01b0387169063f670dde190610547908890889088908890600401612c32565b60206040518083038186803b15801561055f57600080fd5b505afa158015610573573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610597919081019061248f565b9695505050505050565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690634f8e6e23906105eb908690600401612b76565b60206040518083038186803b15801561060357600080fd5b505afa158015610617573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061063b919081019061240f565b6106575760405162461bcd60e51b815260040161035c90612ce8565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038416906323b872dd906106a090339030908790600401612b84565b602060405180830381600087803b1580156106ba57600080fd5b505af11580156106ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106f2919081019061240f565b506040517f42966c680000000000000000000000000000000000000000000000000000000081526001600160a01b038416906342966c6890610738908590600401612dc8565b602060405180830381600087803b15801561075257600080fd5b505af1158015610766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061078a919081019061240f565b50600190505b92915050565b60005460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156107f757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107d9575b5050505050905090565b60005460ff16156108245760405162461bcd60e51b815260040161035c90612c78565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610858336113bb565b60005b828110156108905761088784848381811061087257fe5b90506020020160206102209190810190612266565b5060010161085b565b5061089a81610b40565b505050565b6108a7610b17565b6108c35760405162461bcd60e51b815260040161035c90612d38565b600080546040516101009091046001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b6000610933610b17565b61094f5760405162461bcd60e51b815260040161035c90612d38565b6001600160a01b03821660009081526002602052604090205460ff16156109885760405162461bcd60e51b815260040161035c90612c68565b6001600160a01b0382166109ae5760405162461bcd60e51b815260040161035c90612d28565b6001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03851690811790915560008181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909417909355915190917f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de291a250506001546000190190565b60046020526000908152604090205463ffffffff80821691640100000000810490911690680100000000000000008104600590810b916e0100000000000000000000000000008104820b91740100000000000000000000000000000000000000008204900b907a010000000000000000000000000000000000000000000000000000900460ff1686565b60005461010090046001600160a01b031690565b6000805461010090046001600160a01b0316610b3161145e565b6001600160a01b031614905090565b610b48610b17565b610b645760405162461bcd60e51b815260040161035c90612d38565b6001600160a01b038116610b8a5760405162461bcd60e51b815260040161035c90612da8565b6005546040516001600160a01b03918216918316907fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5090600090a3600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6001600160a01b03851660009081526002602052604081205460ff16610c375760405162461bcd60e51b815260040161035c90612d08565b6040517f93c7e3bc0000000000000000000000000000000000000000000000000000000081526001600160a01b038716906393c7e3bc90610547908890889088908890600401612c32565b610c8a610b17565b610ca65760405162461bcd60e51b815260040161035c90612d38565b610caf81611462565b6000826001600160a01b031660001b84189050816004600083815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548165ffffffffffff021916908360050b65ffffffffffff160217905550606082015181600001600e6101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060808201518160000160146101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060a082015181600001601a6101000a81548160ff021916908360ff160217905550905050610ea582600360008481526020019081526020016000206040518060a00160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160089054906101000a900460050b60050b60050b815260200160008201600e9054906101000a900460050b60050b60050b81526020016000820160149054906101000a900460050b60050b60050b8152505061150890919063ffffffff16565b60009182526003602090815260409283902082518154928401519484015160608501516080909501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090941663ffffffff928316177fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff166401000000009290961691909102949094177fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff1668010000000000000000600595860b65ffffffffffff90811691909102919091177fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000094860b821694909402939093177fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000009290940b9290921602919091179055505050565b6001818154811061100a57fe5b6000918252602090912001546001600160a01b0316905081565b6005546001600160a01b031681565b6001600160a01b03861660009081526002602052604081205460ff1661106b5760405162461bcd60e51b815260040161035c90612d08565b6040517fd3385d050000000000000000000000000000000000000000000000000000000081526001600160a01b0388169063d3385d05906110b6908990899089908990600401612c32565b602060405180830381600087803b1580156110d057600080fd5b505af11580156110e4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611108919081019061248f565b90508181111561112a5760405162461bcd60e51b815260040161035c90612ca8565b611137868683878761155d565b611142338683611612565b61114d3385856118ec565b846001600160a01b0316336001600160a01b0316877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a8886896040516111979493929190612bd5565b60405180910390a49695505050505050565b60026020526000908152604090205460ff1681565b6001600160a01b03861660009081526002602052604081205460ff166111f65760405162461bcd60e51b815260040161035c90612d08565b6040517f42bfc99c0000000000000000000000000000000000000000000000000000000081526001600160a01b038816906342bfc99c90611241908990899089908990600401612c32565b602060405180830381600087803b15801561125b57600080fd5b505af115801561126f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611293919081019061248f565b9050818110156112b55760405162461bcd60e51b815260040161035c90612d78565b6112c2868685878561155d565b6112cd338685611612565b6112d83385836118ec565b846001600160a01b0316336001600160a01b0316877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a8888876040516111979493929190612bd5565b60036020526000908152604090205463ffffffff80821691640100000000810490911690680100000000000000008104600590810b916e0100000000000000000000000000008104820b9174010000000000000000000000000000000000000000909104900b85565b611393610b17565b6113af5760405162461bcd60e51b815260040161035c90612d38565b6113b8816113bb565b50565b6001600160a01b0381166113e15760405162461bcd60e51b815260040161035c90612c88565b600080546040516001600160a01b038085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b3390565b60a0810151600216158061147c575060a081015160011615155b6114985760405162461bcd60e51b815260040161035c90612cb8565b60a081015160011615806114b25750805163ffffffff1615155b6114ce5760405162461bcd60e51b815260040161035c90612d18565b60a081015160021615806114ec57506000816020015163ffffffff16115b6113b85760405162461bcd60e51b815260040161035c90612cd8565b6115106120cb565b6000808452602084015260a082015160011661152e57600060408401525b60a082015160021661154257600060608401525b60a082015160041661155657600060808401525b5090919050565b6001600160a01b038481169083167f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8511156115ab5760405162461bcd60e51b815260040161035c90612d48565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156115eb5760405162461bcd60e51b815260040161035c90612d58565b6115f88288188688611abc565b611609818818846000190286611abc565b50505050505050565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634f8e6e239061165b908590600401612b76565b60206040518083038186803b15801561167357600080fd5b505afa158015611687573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116ab919081019061240f565b156117e9576040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038316906323b872dd906116f990869030908690600401612b84565b602060405180830381600087803b15801561171357600080fd5b505af1158015611727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061174b919081019061240f565b506040517f42966c680000000000000000000000000000000000000000000000000000000081526001600160a01b038316906342966c6890611791908490600401612dc8565b602060405180830381600087803b1580156117ab57600080fd5b505af11580156117bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e3919081019061240f565b5061089a565b6005546040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063cae182fe90611832908590600401612b76565b60206040518083038186803b15801561184a57600080fd5b505afa15801561185e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611882919081019061240f565b156118d4576005546040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03808516926323b872dd9261179192889216908690600401612b84565b60405162461bcd60e51b815260040161035c90612db8565b6005546040517f4f8e6e230000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634f8e6e2390611935908590600401612b76565b60206040518083038186803b15801561194d57600080fd5b505afa158015611961573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611985919081019061240f565b156119d1576040517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b038316906340c10f19906117919086908590600401612bac565b6005546040517fcae182fe0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063cae182fe90611a1a908590600401612b76565b60206040518083038186803b158015611a3257600080fd5b505afa158015611a46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a6a919081019061240f565b156118d4576005546040517f6be383fc0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690636be383fc9061179190859087908690600401612bc7565b611ac46120f9565b50600083815260046020908152604091829020825160c081018452905463ffffffff808216835264010000000082041692820192909252680100000000000000008204600590810b810b810b938201939093526e0100000000000000000000000000008204830b830b830b6060820152740100000000000000000000000000000000000000008204830b830b90920b60808301527a010000000000000000000000000000000000000000000000000000900460ff1660a0820181905215611e2157611b8d6120cb565b50600084815260036020908152604091829020825160a081018452905463ffffffff808216835264010000000082041682840152680100000000000000008104600590810b810b810b838601526e0100000000000000000000000000008204810b810b810b606084015274010000000000000000000000000000000000000000909104810b810b900b608082015282517f313ce56700000000000000000000000000000000000000000000000000000000815292519092611cbe92859288926001600160a01b0389169263313ce56792600483810193829003018186803b158015611c7757600080fd5b505afa158015611c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611caf91908101906124ad565b8492919063ffffffff611e2716565b9050611cd0818363ffffffff611f5116565b60008581526003602090815260409182902083518154928501519385015160608601516080909601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090941663ffffffff928316177fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff166401000000009290951691909102939093177fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff1668010000000000000000600594850b65ffffffffffff90811691909102919091177fffffffffffffffffffffffff000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000095850b821695909502949094177fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000009290930b93909316021790555b50505050565b611e2f6120cb565b60008260ff16600a0a8481611e4057fe5b059050657fffffffffff811315611e695760405162461bcd60e51b815260040161035c90612d88565b60008115611e775781611e7a565b60015b60a087015190915060011615611f1b57855187510163ffffffff16421115611ead576000604088015263ffffffff421687525b611ebb876040015182612066565b600590810b900b604088015260a086015160021615611f1b57856020015187602001510163ffffffff16421115611f00576000606088015263ffffffff421660208801525b611f0e876060015182612066565b600590810b900b60608801525b60a086015160041615611f4357611f36876080015182612066565b600590810b900b60808801525b86925050505b949350505050565b60a081015160011615801590611f8f5750816040015160050b81604001516000190260050b1380611f8f5750806040015160050b826040015160050b135b15611fac5760405162461bcd60e51b815260040161035c90612d68565b60a081015160021615801590611fea5750816060015160050b81606001516000190260050b1380611fea5750806060015160050b826060015160050b135b156120075760405162461bcd60e51b815260040161035c90612cf8565b60a0810151600416158015906120455750816080015160050b81608001516000190260050b13806120455750806080015160050b826080015160050b135b156120625760405162461bcd60e51b815260040161035c90612d98565b5050565b6000600582810b9084900b017fffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000181128015906120a85750657fffffffffff8113155b6120c45760405162461bcd60e51b815260040161035c90612c98565b9392505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b803561079081612ef2565b60008083601f84011261214b57600080fd5b50813567ffffffffffffffff81111561216357600080fd5b60208301915083602082028301111561217b57600080fd5b9250929050565b805161079081612f06565b803561079081612f0f565b803561079081612f18565b600060c082840312156121b557600080fd5b6121bf60c0612e72565b905060006121cd8484612245565b82525060206121de84848301612245565b60208301525060406121f284828501612198565b604083015250606061220684828501612198565b606083015250608061221a84828501612198565b60808301525060a061222e84828501612250565b60a08301525092915050565b805161079081612f0f565b803561079081612f21565b803561079081612f2a565b805161079081612f2a565b60006020828403121561227857600080fd5b6000611f49848461212e565b600080600080600060a0868803121561229c57600080fd5b60006122a8888861212e565b95505060206122b98882890161218d565b94505060406122ca8882890161212e565b93505060606122db8882890161212e565b92505060806122ec8882890161218d565b9150509295509295909350565b60008060008060008060c0878903121561231257600080fd5b600061231e898961212e565b965050602061232f89828a0161218d565b955050604061234089828a0161212e565b945050606061235189828a0161212e565b935050608061236289828a0161218d565b92505060a061237389828a0161218d565b9150509295509295509295565b6000806040838503121561239357600080fd5b600061239f858561212e565b92505060206123b08582860161218d565b9150509250929050565b6000806000604084860312156123cf57600080fd5b833567ffffffffffffffff8111156123e657600080fd5b6123f286828701612139565b935093505060206124058682870161212e565b9150509250925092565b60006020828403121561242157600080fd5b6000611f498484612182565b60006020828403121561243f57600080fd5b6000611f49848461218d565b6000806000610100848603121561246157600080fd5b600061246d868661218d565b935050602061247e8682870161212e565b9250506040612405868287016121a3565b6000602082840312156124a157600080fd5b6000611f49848461223a565b6000602082840312156124bf57600080fd5b6000611f49848461225b565b60006124d783836124ee565b505060200190565b6124e881612ee0565b82525050565b6124e881612eac565b600061250282612e9f565b61250c8185612ea3565b935061251783612e99565b8060005b8381101561254557815161252f88826124cb565b975061253a83612e99565b92505060010161251b565b509495945050505050565b6124e881612eb7565b6124e881612ebc565b6124e881612ee7565b6124e881612ebf565b6000612581602b83612ea3565b7f45786368616e676550726f766964657220616c7265616479206578697374732081527f696e20746865206c697374000000000000000000000000000000000000000000602082015260400192915050565b60006125e0601c83612ea3565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b6000612619602683612ea3565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000612678601783612ea3565b7f696e743438206164646974696f6e206f766572666c6f77000000000000000000815260200192915050565b60006126b1601483612ea3565b7f616d6f756e74496e4d6178206578636565646564000000000000000000000000815260200192915050565b60006126ea601983612ea3565b7f4c3120776974686f7574204c30206e6f7420616c6c6f77656400000000000000815260200192915050565b6000612723601c83612ea3565b7f696e64657820646f65736e2774206d617463682070726f766964657200000000815260200192915050565b600061275c602183612ea3565b7f74696d6573746570312063616e2774206265207a65726f20696620616374697681527f6500000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006127bb602483612ea3565b7f546f6b656e206d7573742062652061207265736572766520737461626c65206181527f7373657400000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061281a600b83612ea3565b7f4c31204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612853601f83612ea3565b7f45786368616e676550726f766964657220646f6573206e6f7420657869737400815260200192915050565b600061288c602183612ea3565b7f74696d6573746570302063616e2774206265207a65726f20696620616374697681527f6500000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006128eb602383612ea3565b7f45786368616e676550726f766964657220616464726573732063616e2774206281527f6520300000000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061294a602083612ea3565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b6000612983601283612ea3565b7f616d6f756e74496e20746f6f206c617267650000000000000000000000000000815260200192915050565b60006129bc601383612ea3565b7f616d6f756e744f757420746f6f206c6172676500000000000000000000000000815260200192915050565b60006129f5600b83612ea3565b7f4c30204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612a2e601483612ea3565b7f616d6f756e744f75744d696e206e6f74206d6574000000000000000000000000815260200192915050565b6000612a67600f83612ea3565b7f64466c6f7720746f6f206c617267650000000000000000000000000000000000815260200192915050565b6000612aa0600b83612ea3565b7f4c47204578636565646564000000000000000000000000000000000000000000815260200192915050565b6000612ad9601b83612ea3565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000612b12602983612ea3565b7f546f6b656e206d75737420626520737461626c65206f7220636f6c6c6174657281527f616c206173736572740000000000000000000000000000000000000000000000602082015260400192915050565b6124e881612ed1565b6124e881612eda565b6020810161079082846124ee565b60608101612b9282866124df565b612b9f60208301856124ee565b611f496040830184612559565b60408101612bba82856124df565b6120c46020830184612559565b60608101612b9282866124ee565b60808101612be382876124ee565b612bf060208301866124ee565b612bfd6040830185612559565b612c0a6060830184612559565b95945050505050565b602080825281016120c481846124f7565b602081016107908284612550565b60808101612c408287612559565b612c4d60208301866124ee565b612bfd60408301856124ee565b602081016107908284612562565b6020808252810161079081612574565b60208082528101610790816125d3565b602080825281016107908161260c565b602080825281016107908161266b565b60208082528101610790816126a4565b60208082528101610790816126dd565b6020808252810161079081612716565b602080825281016107908161274f565b60208082528101610790816127ae565b602080825281016107908161280d565b6020808252810161079081612846565b602080825281016107908161287f565b60208082528101610790816128de565b602080825281016107908161293d565b6020808252810161079081612976565b60208082528101610790816129af565b60208082528101610790816129e8565b6020808252810161079081612a21565b6020808252810161079081612a5a565b6020808252810161079081612a93565b6020808252810161079081612acc565b6020808252810161079081612b05565b602081016107908284612559565b60a08101612de48288612b64565b612df16020830187612b64565b612dfe604083018661256b565b612e0b606083018561256b565b610597608083018461256b565b60c08101612e268289612b64565b612e336020830188612b64565b612e40604083018761256b565b612e4d606083018661256b565b612e5a608083018561256b565b612e6760a0830184612b6d565b979650505050505050565b60405181810167ffffffffffffffff81118282101715612e9157600080fd5b604052919050565b60200190565b5190565b90815260200190565b600061079082612ec5565b151590565b90565b60050b90565b6001600160a01b031690565b63ffffffff1690565b60ff1690565b6000610790825b600061079082612eac565b612efb81612eac565b81146113b857600080fd5b612efb81612eb7565b612efb81612ebc565b612efb81612ebf565b612efb81612ed1565b612efb81612eda56fea365627a7a7231582067f8831f916fddf7b86c3a3cf24b9b5ecc4ad9361173def8fcd4c058c8965a616c6578706572696d656e74616cf564736f6c63430005110040