Address Details
contract

0x47388847a874472b5615EEBcee416c8Fb4FC12a4

Contract Name
Broker
Creator
0x278160–50fc38 at 0xa82a10–a686cc
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
14227374
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-12-11T02:57:26.788447Z

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