Address Details
contract

0x766ED62ca6725A34778f1D94C02535e9aaFB0491

Contract Name
Broker
Creator
0x278160–50fc38 at 0xe104fc–7c9b01
Balance
0 CELO
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
1 Transactions
Transfers
0 Transfers
Gas Used
28,881
Last Balance Update
14210503
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
2022-11-28T18:00:17.284485Z

contracts/Broker.sol

pragma solidity ^0.5.13;
pragma experimental ABIEncoderV2;

import { IERC20 } from "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
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 { Initializable } from "./common/Initializable.sol";

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

  address[] public exchangeProviders;
  mapping(address => bool) public isExchangeProvider;

  // Address of the reserve.
  IReserve public reserve;

  /* ==================== 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 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 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");
    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");
    transferIn(msg.sender, tokenIn, amountIn);
    transferOut(msg.sender, tokenOut, amountOut);
    emit Swap(exchangeProvider, exchangeId, msg.sender, tokenIn, tokenOut, amountIn, amountOut);
  }

  /* ==================== 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.transferCollateralAsset(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)) {
      IERC20(token).transferFrom(from, address(this), amount);
      IStableToken(token).burn(amount);
    } else if (reserve.isCollateralAsset(token)) {
      IERC20(token).transferFrom(from, address(reserve), amount);
    } else {
      revert("Token must be stable or collateral assert");
    }
  }

  /* ==================== 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/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol

pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `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/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/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;
    }
}
          

/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
    );

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

/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);
}
          

/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);
}
          

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

/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 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 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);
}
          

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

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":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"exchangeProviders","inputs":[{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"nonpayable","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":false},{"type":"function","stateMutability":"nonpayable","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":false},{"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":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}],"constant":false}]
              

Contract Creation Code

0x60806040523480156200001157600080fd5b5060405162001e1638038062001e168339810160408190526200003491620000cf565b808062000049576000805460ff191660011790555b5060006200005f6001600160e01b03620000b816565b60008054610100600160a81b0319166101006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3505062000117565b3390565b8051620000c981620000fd565b92915050565b600060208284031215620000e257600080fd5b6000620000f08484620000bc565b949350505050565b151590565b6200010881620000f8565b81146200011457600080fd5b50565b611cef80620001276000396000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c80638f32d59b116100b2578063cd3293de11610081578063d1d786b111610066578063d1d786b114610234578063ddbbe85014610247578063f2fde38b1461025a5761011b565b8063cd3293de1461020c578063d163b135146102215761011b565b80638f32d59b146101cb5780639cecc80a146101d3578063a20f2305146101e6578063c4454fdc146101f95761011b565b8063462d0b2e116100ee578063462d0b2e14610188578063715018a61461019b57806373ec4cf4146101a35780638da5cb5b146101b65761011b565b806304710d531461012057806304e4564014610135578063158ef93e1461015e5780632cac256814610173575b600080fd5b61013361012e3660046115ea565b61026d565b005b6101486101433660046114ee565b610483565b6040516101559190611c27565b60405180910390f35b61016661057a565b6040516101559190611b33565b61017b610583565b6040516101559190611b22565b610133610196366004611624565b6105f2565b610133610690565b6101486101b13660046114c8565b610727565b6101be6108bc565b6040516101559190611a7e565b6101666108dd565b6101336101e13660046114c8565b610920565b6101486101f43660046114ee565b610a06565b6101be610207366004611697565b610aa3565b610214610ad7565b6040516101559190611b69565b61014861022f366004611563565b610af3565b6101666102423660046114c8565b610c90565b610148610255366004611563565b610ca5565b6101336102683660046114c8565b610e30565b6102756108dd565b61029a5760405162461bcd60e51b815260040161029190611be7565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600182815481106102be57fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16146102fd5760405162461bcd60e51b815260040161029190611bb7565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811061032d57fe5b6000918252602090912001546001805473ffffffffffffffffffffffffffffffffffffffff909216918390811061036057fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018054806103b357fe5b6000828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905590920190925573ffffffffffffffffffffffffffffffffffffffff84168083526002909152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602052604081205460ff166104c85760405162461bcd60e51b815260040161029190611bc7565b6040517ff670dde100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063f670dde190610520908890889088908890600401611b41565b60206040518083038186803b15801561053857600080fd5b505afa15801561054c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061057091908101906116b5565b9695505050505050565b60005460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156105e857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116105bd575b5050505050905090565b60005460ff16156106155760405162461bcd60e51b815260040161029190611b87565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561064933610e60565b60005b828110156106815761067884848381811061066357fe5b90506020020160206101b191908101906114c8565b5060010161064c565b5061068b81610920565b505050565b6106986108dd565b6106b45760405162461bcd60e51b815260040161029190611be7565b6000805460405161010090910473ffffffffffffffffffffffffffffffffffffffff16907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b60006107316108dd565b61074d5760405162461bcd60e51b815260040161029190611be7565b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604090205460ff16156107935760405162461bcd60e51b815260040161029190611b77565b73ffffffffffffffffffffffffffffffffffffffff82166107c65760405162461bcd60e51b815260040161029190611bd7565b6001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915560008181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909417909355915190917f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de291a250506001547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b600054610100900473ffffffffffffffffffffffffffffffffffffffff1690565b60008054610100900473ffffffffffffffffffffffffffffffffffffffff16610904610f2a565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6109286108dd565b6109445760405162461bcd60e51b815260040161029190611be7565b73ffffffffffffffffffffffffffffffffffffffff81166109775760405162461bcd60e51b815260040161029190611c07565b60035460405173ffffffffffffffffffffffffffffffffffffffff918216918316907fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5090600090a3600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602052604081205460ff16610a4b5760405162461bcd60e51b815260040161029190611bc7565b6040517f93c7e3bc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906393c7e3bc90610520908890889088908890600401611b41565b60018181548110610ab057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081205460ff16610b385760405162461bcd60e51b815260040161029190611bc7565b6040517fd3385d0500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88169063d3385d0590610b90908990899089908990600401611b41565b602060405180830381600087803b158015610baa57600080fd5b505af1158015610bbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610be291908101906116b5565b905081811115610c045760405162461bcd60e51b815260040161029190611ba7565b610c0f338683610f2e565b610c1a338585611249565b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a888689604051610c7e9493929190611ae4565b60405180910390a49695505050505050565b60026020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081205460ff16610cea5760405162461bcd60e51b815260040161029190611bc7565b6040517f42bfc99c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906342bfc99c90610d42908990899089908990600401611b41565b602060405180830381600087803b158015610d5c57600080fd5b505af1158015610d70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d9491908101906116b5565b905081811015610db65760405162461bcd60e51b815260040161029190611bf7565b610dc1338685610f2e565b610dcc338583611249565b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a888887604051610c7e9493929190611ae4565b610e386108dd565b610e545760405162461bcd60e51b815260040161029190611be7565b610e5d81610e60565b50565b73ffffffffffffffffffffffffffffffffffffffff8116610e935760405162461bcd60e51b815260040161029190611b97565b6000805460405173ffffffffffffffffffffffffffffffffffffffff8085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b3390565b6003546040517f4f8e6e2300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634f8e6e2390610f84908590600401611a7e565b60206040518083038186803b158015610f9c57600080fd5b505afa158015610fb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fd49190810190611679565b1561112c576040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906323b872dd9061102f90869030908690600401611a8c565b602060405180830381600087803b15801561104957600080fd5b505af115801561105d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110819190810190611679565b506040517f42966c6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906342966c68906110d4908490600401611c27565b602060405180830381600087803b1580156110ee57600080fd5b505af1158015611102573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111269190810190611679565b5061068b565b6003546040517fcae182fe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063cae182fe90611182908590600401611a7e565b60206040518083038186803b15801561119a57600080fd5b505afa1580156111ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111d29190810190611679565b15611231576003546040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808516926323b872dd926110d492889216908690600401611a8c565b60405162461bcd60e51b815260040161029190611c17565b6003546040517f4f8e6e2300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634f8e6e239061129f908590600401611a7e565b60206040518083038186803b1580156112b757600080fd5b505afa1580156112cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112ef9190810190611679565b15611348576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906340c10f19906110d49086908590600401611ab4565b6003546040517fcae182fe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063cae182fe9061139e908590600401611a7e565b60206040518083038186803b1580156113b657600080fd5b505afa1580156113ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113ee9190810190611679565b15611231576003546040517f042b7a5400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063042b7a54906110d490859087908690600401611ad6565b803561145881611c86565b92915050565b60008083601f84011261147057600080fd5b50813567ffffffffffffffff81111561148857600080fd5b6020830191508360208202830111156114a057600080fd5b9250929050565b805161145881611c9a565b803561145881611ca3565b805161145881611ca3565b6000602082840312156114da57600080fd5b60006114e6848461144d565b949350505050565b600080600080600060a0868803121561150657600080fd5b6000611512888861144d565b9550506020611523888289016114b2565b94505060406115348882890161144d565b93505060606115458882890161144d565b9250506080611556888289016114b2565b9150509295509295909350565b60008060008060008060c0878903121561157c57600080fd5b6000611588898961144d565b965050602061159989828a016114b2565b95505060406115aa89828a0161144d565b94505060606115bb89828a0161144d565b93505060806115cc89828a016114b2565b92505060a06115dd89828a016114b2565b9150509295509295509295565b600080604083850312156115fd57600080fd5b6000611609858561144d565b925050602061161a858286016114b2565b9150509250929050565b60008060006040848603121561163957600080fd5b833567ffffffffffffffff81111561165057600080fd5b61165c8682870161145e565b9350935050602061166f8682870161144d565b9150509250925092565b60006020828403121561168b57600080fd5b60006114e684846114a7565b6000602082840312156116a957600080fd5b60006114e684846114b2565b6000602082840312156116c757600080fd5b60006114e684846114bd565b60006116df83836116f6565b505060200190565b6116f081611c74565b82525050565b6116f081611c48565b600061170a82611c3b565b6117148185611c3f565b935061171f83611c35565b8060005b8381101561174d57815161173788826116d3565b975061174283611c35565b925050600101611723565b509495945050505050565b6116f081611c53565b6116f081611c58565b6116f081611c7b565b6000611780602b83611c3f565b7f45786368616e676550726f766964657220616c7265616479206578697374732081527f696e20746865206c697374000000000000000000000000000000000000000000602082015260400192915050565b60006117df601c83611c3f565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b6000611818602683611c3f565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000611877601483611c3f565b7f616d6f756e74496e4d6178206578636565646564000000000000000000000000815260200192915050565b60006118b0601c83611c3f565b7f696e64657820646f65736e2774206d617463682070726f766964657200000000815260200192915050565b60006118e9601f83611c3f565b7f45786368616e676550726f766964657220646f6573206e6f7420657869737400815260200192915050565b6000611922602383611c3f565b7f45786368616e676550726f766964657220616464726573732063616e2774206281527f6520300000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000611981602083611c3f565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b60006119ba601483611c3f565b7f616d6f756e744f75744d696e206e6f74206d6574000000000000000000000000815260200192915050565b60006119f3601b83611c3f565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000611a2c602983611c3f565b7f546f6b656e206d75737420626520737461626c65206f7220636f6c6c6174657281527f616c206173736572740000000000000000000000000000000000000000000000602082015260400192915050565b6020810161145882846116f6565b60608101611a9a82866116e7565b611aa760208301856116f6565b6114e66040830184611761565b60408101611ac282856116e7565b611acf6020830184611761565b9392505050565b60608101611a9a82866116f6565b60808101611af282876116f6565b611aff60208301866116f6565b611b0c6040830185611761565b611b196060830184611761565b95945050505050565b60208082528101611acf81846116ff565b602081016114588284611758565b60808101611b4f8287611761565b611b5c60208301866116f6565b611b0c60408301856116f6565b60208101611458828461176a565b6020808252810161145881611773565b60208082528101611458816117d2565b602080825281016114588161180b565b602080825281016114588161186a565b60208082528101611458816118a3565b60208082528101611458816118dc565b6020808252810161145881611915565b6020808252810161145881611974565b60208082528101611458816119ad565b60208082528101611458816119e6565b6020808252810161145881611a1f565b602081016114588284611761565b60200190565b5190565b90815260200190565b600061145882611c5b565b151590565b90565b73ffffffffffffffffffffffffffffffffffffffff1690565b6000611458825b600061145882611c48565b611c8f81611c48565b8114610e5d57600080fd5b611c8f81611c53565b611c8f81611c5856fea365627a7a72315820ad62bad6a15ad94dc298c1ac4bcaebf91e3d82a236d0189b975e93a2cd9c7c876c6578706572696d656e74616cf564736f6c634300051100400000000000000000000000000000000000000000000000000000000000000001

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061011b5760003560e01c80638f32d59b116100b2578063cd3293de11610081578063d1d786b111610066578063d1d786b114610234578063ddbbe85014610247578063f2fde38b1461025a5761011b565b8063cd3293de1461020c578063d163b135146102215761011b565b80638f32d59b146101cb5780639cecc80a146101d3578063a20f2305146101e6578063c4454fdc146101f95761011b565b8063462d0b2e116100ee578063462d0b2e14610188578063715018a61461019b57806373ec4cf4146101a35780638da5cb5b146101b65761011b565b806304710d531461012057806304e4564014610135578063158ef93e1461015e5780632cac256814610173575b600080fd5b61013361012e3660046115ea565b61026d565b005b6101486101433660046114ee565b610483565b6040516101559190611c27565b60405180910390f35b61016661057a565b6040516101559190611b33565b61017b610583565b6040516101559190611b22565b610133610196366004611624565b6105f2565b610133610690565b6101486101b13660046114c8565b610727565b6101be6108bc565b6040516101559190611a7e565b6101666108dd565b6101336101e13660046114c8565b610920565b6101486101f43660046114ee565b610a06565b6101be610207366004611697565b610aa3565b610214610ad7565b6040516101559190611b69565b61014861022f366004611563565b610af3565b6101666102423660046114c8565b610c90565b610148610255366004611563565b610ca5565b6101336102683660046114c8565b610e30565b6102756108dd565b61029a5760405162461bcd60e51b815260040161029190611be7565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600182815481106102be57fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16146102fd5760405162461bcd60e51b815260040161029190611bb7565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811061032d57fe5b6000918252602090912001546001805473ffffffffffffffffffffffffffffffffffffffff909216918390811061036057fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018054806103b357fe5b6000828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905590920190925573ffffffffffffffffffffffffffffffffffffffff84168083526002909152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602052604081205460ff166104c85760405162461bcd60e51b815260040161029190611bc7565b6040517ff670dde100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063f670dde190610520908890889088908890600401611b41565b60206040518083038186803b15801561053857600080fd5b505afa15801561054c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061057091908101906116b5565b9695505050505050565b60005460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156105e857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116105bd575b5050505050905090565b60005460ff16156106155760405162461bcd60e51b815260040161029190611b87565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561064933610e60565b60005b828110156106815761067884848381811061066357fe5b90506020020160206101b191908101906114c8565b5060010161064c565b5061068b81610920565b505050565b6106986108dd565b6106b45760405162461bcd60e51b815260040161029190611be7565b6000805460405161010090910473ffffffffffffffffffffffffffffffffffffffff16907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff169055565b60006107316108dd565b61074d5760405162461bcd60e51b815260040161029190611be7565b73ffffffffffffffffffffffffffffffffffffffff821660009081526002602052604090205460ff16156107935760405162461bcd60e51b815260040161029190611b77565b73ffffffffffffffffffffffffffffffffffffffff82166107c65760405162461bcd60e51b815260040161029190611bd7565b6001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915560008181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909417909355915190917f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de291a250506001547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b600054610100900473ffffffffffffffffffffffffffffffffffffffff1690565b60008054610100900473ffffffffffffffffffffffffffffffffffffffff16610904610f2a565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b6109286108dd565b6109445760405162461bcd60e51b815260040161029190611be7565b73ffffffffffffffffffffffffffffffffffffffff81166109775760405162461bcd60e51b815260040161029190611c07565b60035460405173ffffffffffffffffffffffffffffffffffffffff918216918316907fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5090600090a3600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff851660009081526002602052604081205460ff16610a4b5760405162461bcd60e51b815260040161029190611bc7565b6040517f93c7e3bc00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906393c7e3bc90610520908890889088908890600401611b41565b60018181548110610ab057fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905081565b60035473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081205460ff16610b385760405162461bcd60e51b815260040161029190611bc7565b6040517fd3385d0500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88169063d3385d0590610b90908990899089908990600401611b41565b602060405180830381600087803b158015610baa57600080fd5b505af1158015610bbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610be291908101906116b5565b905081811115610c045760405162461bcd60e51b815260040161029190611ba7565b610c0f338683610f2e565b610c1a338585611249565b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a888689604051610c7e9493929190611ae4565b60405180910390a49695505050505050565b60026020526000908152604090205460ff1681565b73ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081205460ff16610cea5760405162461bcd60e51b815260040161029190611bc7565b6040517f42bfc99c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906342bfc99c90610d42908990899089908990600401611b41565b602060405180830381600087803b158015610d5c57600080fd5b505af1158015610d70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d9491908101906116b5565b905081811015610db65760405162461bcd60e51b815260040161029190611bf7565b610dc1338685610f2e565b610dcc338583611249565b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16877fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f8a888887604051610c7e9493929190611ae4565b610e386108dd565b610e545760405162461bcd60e51b815260040161029190611be7565b610e5d81610e60565b50565b73ffffffffffffffffffffffffffffffffffffffff8116610e935760405162461bcd60e51b815260040161029190611b97565b6000805460405173ffffffffffffffffffffffffffffffffffffffff8085169361010090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b3390565b6003546040517f4f8e6e2300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634f8e6e2390610f84908590600401611a7e565b60206040518083038186803b158015610f9c57600080fd5b505afa158015610fb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fd49190810190611679565b1561112c576040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906323b872dd9061102f90869030908690600401611a8c565b602060405180830381600087803b15801561104957600080fd5b505af115801561105d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110819190810190611679565b506040517f42966c6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906342966c68906110d4908490600401611c27565b602060405180830381600087803b1580156110ee57600080fd5b505af1158015611102573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111269190810190611679565b5061068b565b6003546040517fcae182fe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063cae182fe90611182908590600401611a7e565b60206040518083038186803b15801561119a57600080fd5b505afa1580156111ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111d29190810190611679565b15611231576003546040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808516926323b872dd926110d492889216908690600401611a8c565b60405162461bcd60e51b815260040161029190611c17565b6003546040517f4f8e6e2300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690634f8e6e239061129f908590600401611a7e565b60206040518083038186803b1580156112b757600080fd5b505afa1580156112cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112ef9190810190611679565b15611348576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906340c10f19906110d49086908590600401611ab4565b6003546040517fcae182fe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063cae182fe9061139e908590600401611a7e565b60206040518083038186803b1580156113b657600080fd5b505afa1580156113ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113ee9190810190611679565b15611231576003546040517f042b7a5400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063042b7a54906110d490859087908690600401611ad6565b803561145881611c86565b92915050565b60008083601f84011261147057600080fd5b50813567ffffffffffffffff81111561148857600080fd5b6020830191508360208202830111156114a057600080fd5b9250929050565b805161145881611c9a565b803561145881611ca3565b805161145881611ca3565b6000602082840312156114da57600080fd5b60006114e6848461144d565b949350505050565b600080600080600060a0868803121561150657600080fd5b6000611512888861144d565b9550506020611523888289016114b2565b94505060406115348882890161144d565b93505060606115458882890161144d565b9250506080611556888289016114b2565b9150509295509295909350565b60008060008060008060c0878903121561157c57600080fd5b6000611588898961144d565b965050602061159989828a016114b2565b95505060406115aa89828a0161144d565b94505060606115bb89828a0161144d565b93505060806115cc89828a016114b2565b92505060a06115dd89828a016114b2565b9150509295509295509295565b600080604083850312156115fd57600080fd5b6000611609858561144d565b925050602061161a858286016114b2565b9150509250929050565b60008060006040848603121561163957600080fd5b833567ffffffffffffffff81111561165057600080fd5b61165c8682870161145e565b9350935050602061166f8682870161144d565b9150509250925092565b60006020828403121561168b57600080fd5b60006114e684846114a7565b6000602082840312156116a957600080fd5b60006114e684846114b2565b6000602082840312156116c757600080fd5b60006114e684846114bd565b60006116df83836116f6565b505060200190565b6116f081611c74565b82525050565b6116f081611c48565b600061170a82611c3b565b6117148185611c3f565b935061171f83611c35565b8060005b8381101561174d57815161173788826116d3565b975061174283611c35565b925050600101611723565b509495945050505050565b6116f081611c53565b6116f081611c58565b6116f081611c7b565b6000611780602b83611c3f565b7f45786368616e676550726f766964657220616c7265616479206578697374732081527f696e20746865206c697374000000000000000000000000000000000000000000602082015260400192915050565b60006117df601c83611c3f565b7f636f6e747261637420616c726561647920696e697469616c697a656400000000815260200192915050565b6000611818602683611c3f565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181527f6464726573730000000000000000000000000000000000000000000000000000602082015260400192915050565b6000611877601483611c3f565b7f616d6f756e74496e4d6178206578636565646564000000000000000000000000815260200192915050565b60006118b0601c83611c3f565b7f696e64657820646f65736e2774206d617463682070726f766964657200000000815260200192915050565b60006118e9601f83611c3f565b7f45786368616e676550726f766964657220646f6573206e6f7420657869737400815260200192915050565b6000611922602383611c3f565b7f45786368616e676550726f766964657220616464726573732063616e2774206281527f6520300000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000611981602083611c3f565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572815260200192915050565b60006119ba601483611c3f565b7f616d6f756e744f75744d696e206e6f74206d6574000000000000000000000000815260200192915050565b60006119f3601b83611c3f565b7f526573657276652061646472657373206d757374206265207365740000000000815260200192915050565b6000611a2c602983611c3f565b7f546f6b656e206d75737420626520737461626c65206f7220636f6c6c6174657281527f616c206173736572740000000000000000000000000000000000000000000000602082015260400192915050565b6020810161145882846116f6565b60608101611a9a82866116e7565b611aa760208301856116f6565b6114e66040830184611761565b60408101611ac282856116e7565b611acf6020830184611761565b9392505050565b60608101611a9a82866116f6565b60808101611af282876116f6565b611aff60208301866116f6565b611b0c6040830185611761565b611b196060830184611761565b95945050505050565b60208082528101611acf81846116ff565b602081016114588284611758565b60808101611b4f8287611761565b611b5c60208301866116f6565b611b0c60408301856116f6565b60208101611458828461176a565b6020808252810161145881611773565b60208082528101611458816117d2565b602080825281016114588161180b565b602080825281016114588161186a565b60208082528101611458816118a3565b60208082528101611458816118dc565b6020808252810161145881611915565b6020808252810161145881611974565b60208082528101611458816119ad565b60208082528101611458816119e6565b6020808252810161145881611a1f565b602081016114588284611761565b60200190565b5190565b90815260200190565b600061145882611c5b565b151590565b90565b73ffffffffffffffffffffffffffffffffffffffff1690565b6000611458825b600061145882611c48565b611c8f81611c48565b8114610e5d57600080fd5b611c8f81611c53565b611c8f81611c5856fea365627a7a72315820ad62bad6a15ad94dc298c1ac4bcaebf91e3d82a236d0189b975e93a2cd9c7c876c6578706572696d656e74616cf564736f6c63430005110040