CELO Price: $0.12 (+0.18%)
Gas: 25 GWei

Contract

0xa92Fb401f6aFBBF27338d4cE20322b102b35c51A

Overview

CELO Balance

Celo Mainnet LogoCelo Mainnet LogoCelo Mainnet Logo0 CELO

CELO Value

$0.00

More Info

Private Name Tags

Multichain Info

Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Broker

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

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

import { Ownable } from "openzeppelin-contracts-next/contracts/access/Ownable.sol";
import { SafeERC20MintableBurnable } from "contracts/common/SafeERC20MintableBurnable.sol";
import { IERC20MintableBurnable as IERC20 } from "contracts/common/IERC20MintableBurnable.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 { ITradingLimits } from "../interfaces/ITradingLimits.sol";

import { TradingLimits } from "../libraries/TradingLimits.sol";
import { Initializable } from "celo/contracts/common/Initializable.sol";
import { ReentrancyGuard } from "openzeppelin-contracts-next/contracts/security/ReentrancyGuard.sol";

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

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

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

  /// @inheritdoc IBroker
  address[] public exchangeProviders;

  /// @inheritdoc IBroker
  mapping(address => bool) public isExchangeProvider;
  mapping(bytes32 => ITradingLimits.State) public tradingLimitsState;
  mapping(bytes32 => ITradingLimits.Config) public tradingLimitsConfig;

  // Deprecated address of the reserve. Kept to keep storage layout consistent with previous versions.
  // slither-disable-next-line constable-states
  uint256 public __deprecated0; // prev: IReserve public reserve;

  uint256 private constant MAX_INT256 = uint256(type(int256).max);

  mapping(address => address) public exchangeReserve;
  /* ==================== Constructor ==================== */

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

  /// @inheritdoc IBroker
  function initialize(address[] calldata _exchangeProviders, address[] calldata _reserves) external initializer {
    _transferOwnership(msg.sender);
    for (uint256 i = 0; i < _exchangeProviders.length; i++) {
      addExchangeProvider(_exchangeProviders[i], _reserves[i]);
    }
  }

  /// @inheritdoc IBroker
  function setReserves(
    address[] calldata _exchangeProviders,
    address[] calldata _reserves
  ) external override(IBroker, IBrokerAdmin) onlyOwner {
    for (uint256 i = 0; i < _exchangeProviders.length; i++) {
      require(isExchangeProvider[_exchangeProviders[i]], "ExchangeProvider does not exist");
      require(_reserves[i] != address(0), "Reserve address can't be 0");
      exchangeReserve[_exchangeProviders[i]] = _reserves[i];
      emit ReserveSet(_exchangeProviders[i], _reserves[i]);
    }
  }

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

  /// @inheritdoc IBroker
  function addExchangeProvider(
    address exchangeProvider,
    address reserve
  ) public override(IBroker, IBrokerAdmin) onlyOwner returns (uint256 index) {
    require(!isExchangeProvider[exchangeProvider], "ExchangeProvider already exists in the list");
    require(exchangeProvider != address(0), "ExchangeProvider address can't be 0");
    require(reserve != address(0), "Reserve address can't be 0");
    exchangeProviders.push(exchangeProvider);
    isExchangeProvider[exchangeProvider] = true;
    exchangeReserve[exchangeProvider] = reserve;
    emit ExchangeProviderAdded(exchangeProvider);
    emit ReserveSet(exchangeProvider, reserve);
    index = exchangeProviders.length - 1;
  }

  /// @inheritdoc IBroker
  function removeExchangeProvider(
    address exchangeProvider,
    uint256 index
  ) public override(IBroker, IBrokerAdmin) onlyOwner {
    require(exchangeProviders[index] == exchangeProvider, "index doesn't match provider");
    exchangeProviders[index] = exchangeProviders[exchangeProviders.length - 1];
    exchangeProviders.pop();
    delete isExchangeProvider[exchangeProvider];
    delete exchangeReserve[exchangeProvider];
    emit ExchangeProviderRemoved(exchangeProvider);
  }

  /// @inheritdoc IBroker
  function getAmountIn(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut
  ) external view returns (uint256 amountIn) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    address reserve = exchangeReserve[exchangeProvider];
    if (IReserve(reserve).isCollateralAsset(tokenOut)) {
      require(IERC20(tokenOut).balanceOf(reserve) >= amountOut, "Insufficient balance in reserve");
    }
    amountIn = IExchangeProvider(exchangeProvider).getAmountIn(exchangeId, tokenIn, tokenOut, amountOut);
  }

  /// @inheritdoc IBroker
  function getAmountOut(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external view returns (uint256 amountOut) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    amountOut = IExchangeProvider(exchangeProvider).getAmountOut(exchangeId, tokenIn, tokenOut, amountIn);
    address reserve = exchangeReserve[exchangeProvider];
    if (IReserve(reserve).isCollateralAsset(tokenOut)) {
      require(IERC20(tokenOut).balanceOf(reserve) >= amountOut, "Insufficient balance in reserve");
    }
  }

  /// @inheritdoc IBroker
  function swapIn(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOutMin
  ) external nonReentrant returns (uint256 amountOut) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    // slither-disable-next-line reentrancy-benign
    amountOut = IExchangeProvider(exchangeProvider).swapIn(exchangeId, tokenIn, tokenOut, amountIn);
    require(amountOut >= amountOutMin, "amountOutMin not met");
    guardTradingLimits(exchangeId, tokenIn, amountIn, tokenOut, amountOut);

    address reserve = exchangeReserve[exchangeProvider];
    transferIn(payable(msg.sender), tokenIn, amountIn, reserve);
    transferOut(payable(msg.sender), tokenOut, amountOut, reserve);
    emit Swap(exchangeProvider, exchangeId, msg.sender, tokenIn, tokenOut, amountIn, amountOut);
  }

  /// @inheritdoc IBroker
  function swapOut(
    address exchangeProvider,
    bytes32 exchangeId,
    address tokenIn,
    address tokenOut,
    uint256 amountOut,
    uint256 amountInMax
  ) external nonReentrant returns (uint256 amountIn) {
    require(isExchangeProvider[exchangeProvider], "ExchangeProvider does not exist");
    // slither-disable-next-line reentrancy-benign
    amountIn = IExchangeProvider(exchangeProvider).swapOut(exchangeId, tokenIn, tokenOut, amountOut);
    require(amountIn <= amountInMax, "amountInMax exceeded");
    guardTradingLimits(exchangeId, tokenIn, amountIn, tokenOut, amountOut);

    address reserve = exchangeReserve[exchangeProvider];
    transferIn(payable(msg.sender), tokenIn, amountIn, reserve);
    transferOut(payable(msg.sender), tokenOut, amountOut, reserve);
    emit Swap(exchangeProvider, exchangeId, msg.sender, tokenIn, tokenOut, amountIn, amountOut);
  }

  /// @inheritdoc IBroker
  function burnStableTokens(address token, uint256 amount) public returns (bool) {
    IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
    IERC20(token).safeBurn(amount);
    return true;
  }

  /// @inheritdoc IBroker
  function configureTradingLimit(
    bytes32 exchangeId,
    address token,
    ITradingLimits.Config memory config
  ) external onlyOwner {
    config.validate();

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

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

  /**
   * @notice Transfer a specified Mento asset to the given address.
   * If the specified asset is a stable asset it will be minted directly to the address. If
   * the asset is a collateral asset it will be transferred from the reserve to the given address.
   * @param to The address receiving the asset.
   * @param token The asset to transfer.
   * @param amount The amount of `token` to be transferred.
   * @param _reserve The address of the corresponding reserve.
   */
  function transferOut(address payable to, address token, uint256 amount, address _reserve) internal {
    IReserve reserve = IReserve(_reserve);
    if (reserve.isStableAsset(token)) {
      IERC20(token).safeMint(to, amount);
    } else if (reserve.isCollateralAsset(token)) {
      require(reserve.transferExchangeCollateralAsset(token, to, amount), "Transfer of the collateral asset failed");
    } 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.
   * @param _reserve The address of the corresponding reserve.
   */
  function transferIn(address payable from, address token, uint256 amount, address _reserve) internal {
    IReserve reserve = IReserve(_reserve);
    if (reserve.isStableAsset(token)) {
      IERC20(token).safeTransferFrom(from, address(this), amount);
      IERC20(token).safeBurn(amount);
    } else if (reserve.isCollateralAsset(token)) {
      IERC20(token).safeTransferFrom(from, address(reserve), amount);
    } else {
      revert("Token must be stable or collateral assert");
    }
  }

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/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.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * 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.
 */
abstract 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() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the 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 virtual onlyOwner {
        _transferOwnership(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 virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

pragma solidity ^0.8.0;

import { IERC20MintableBurnable as IERC20 } from "contracts/common/IERC20MintableBurnable.sol";
import { Address } from "openzeppelin-contracts-next/contracts/utils/Address.sol";

/**
 * @title SafeERC20MintableBurnable
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20MintableBurnable for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20MintableBurnable {
  using Address for address;

  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  }

  function safeMint(IERC20 token, address account, uint256 amount) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.mint.selector, account, amount));
  }

  function safeBurn(IERC20 token, uint256 amount) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.burn.selector, amount));
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
   * on the return value: the return value is optional (but if data is returned, it must not be false).
   * @param token The token targeted by the call.
   * @param data The call data (encoded using abi.encode or one of its variants).
   */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.

    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }
}

pragma solidity ^0.8.0;

import "openzeppelin-contracts-next/contracts/token/ERC20/IERC20.sol";

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20MintableBurnable is IERC20 {
  function mint(address account, uint256 amount) external;
  function burn(uint256 amount) external;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >0.5.13 <0.9;
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 pool 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);
}

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

import { ITradingLimits } from "./ITradingLimits.sol";

/*
 * @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 Emitted when a new trading limit is configured.
   * @param exchangeId the exchangeId to target.
   * @param token the token to target.
   * @param config the new trading limits config.
   */
  event TradingLimitConfigured(bytes32 exchangeId, address token, ITradingLimits.Config config);

  /**
   * @notice Allows the contract to be upgradable via the proxy.
   * @param _exchangeProviders The addresses of the ExchangeProvider contracts.
   * @param _reserves The address of the Reserve contract.
   */
  function initialize(address[] calldata _exchangeProviders, address[] calldata _reserves) external;

  /**
   * @notice Set the reserves for the exchange providers.
   * @param _exchangeProviders The addresses of the ExchangeProvider contracts.
   * @param _reserves The addresses of the Reserve contracts.
   */
  function setReserves(address[] calldata _exchangeProviders, address[] calldata _reserves) external;

  /**
   * @notice Add an exchange provider to the list of providers.
   * @param exchangeProvider The address of the exchange provider to add.
   * @param reserve The address of the reserve used by the exchange provider.
   * @return index The index of the newly added specified exchange provider.
   */
  function addExchangeProvider(address exchangeProvider, address reserve) external returns (uint256 index);

  /**
   * @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) external;

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

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

  /**
   * @notice 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 Permissionless way to burn stables from msg.sender directly.
   * @param token The token getting burned.
   * @param amount The amount of the token getting burned.
   * @return True if transaction succeeds.
   */
  function burnStableTokens(address token, uint256 amount) external returns (bool);

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

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

  /**
   * @notice Get the address of the exchange provider at a given index.
   * @dev Auto-generated getter for the exchangeProviders array.
   * @param index The index of the exchange provider.
   * @return exchangeProvider The address of the exchange provider.
   */
  function exchangeProviders(uint256 index) external view returns (address exchangeProvider);

  /**
   * @notice Check if a given address is an exchange provider.
   * @dev Auto-generated getter for the isExchangeProvider mapping.
   * @param exchangeProvider The address to check.
   * @return isExchangeProvider True if the address is an exchange provider, false otherwise.
   */
  function isExchangeProvider(address exchangeProvider) external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.5.13 <0.8.19;

/*
 * @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, address reserve) external returns (uint256 index);

  /**
   * @notice Set the reserves for the exchange providers.
   * @param _exchangeProviders The addresses of the ExchangeProvider contracts.
   * @param _reserves The addresses of the Reserve contracts.
   */
  function setReserves(address[] calldata _exchangeProviders, address[] calldata _reserves) external;
}

File 8 of 15 : IReserve.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >0.5.13 <0.9;

interface IReserve {
  function setTobinTaxStalenessThreshold(uint256) external;

  function addToken(address) external returns (bool);

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

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

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

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

  function getReserveGoldBalance() external view returns (uint256);

  function getUnfrozenReserveGoldBalance() external view returns (uint256);

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

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

  function getReserveRatio() external view returns (uint256);

  function addExchangeSpender(address) external;

  function removeExchangeSpender(address, uint256) external;

  function addSpender(address) external;

  function removeSpender(address) external;

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

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

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

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

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

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

  function initialize(
    address registryAddress,
    uint256 _tobinTaxStalenessThreshold,
    uint256 _spendingRatioForCelo,
    uint256 _frozenGold,
    uint256 _frozenDays,
    bytes32[] calldata _assetAllocationSymbols,
    uint256[] calldata _assetAllocationWeights,
    uint256 _tobinTax,
    uint256 _tobinTaxReserveRatio,
    address[] calldata _collateralAssets,
    uint256[] calldata _collateralAssetDailySpendingRatios
  ) external;

  /// @notice IOwnable:
  function transferOwnership(address newOwner) external;

  function renounceOwnership() external;

  function owner() external view returns (address);

  /// @notice Getters:
  function registry() external view returns (address);

  function tobinTaxStalenessThreshold() external view returns (uint256);

  function tobinTax() external view returns (uint256);

  function tobinTaxReserveRatio() external view returns (uint256);

  function getDailySpendingRatio() external view returns (uint256);

  function checkIsCollateralAsset(address collateralAsset) external view returns (bool);

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

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

  function getAssetAllocationSymbols() external view returns (bytes32[] memory);

  function getAssetAllocationWeights() external view returns (uint256[] memory);

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

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

  function getUnfrozenBalance() external view returns (uint256);

  function isOtherReserveAddress(address otherReserveAddress) external view returns (bool);

  function isSpender(address spender) external view returns (bool);

  /// @notice Setters:
  function setRegistry(address) external;

  function setTobinTax(uint256) external;

  function setTobinTaxReserveRatio(uint256) external;

  function setDailySpendingRatio(uint256 spendingRatio) external;

  function setDailySpendingRatioForCollateralAssets(
    address[] calldata _collateralAssets,
    uint256[] calldata collateralAssetDailySpendingRatios
  ) external;

  function setFrozenGold(uint256 frozenGold, uint256 frozenDays) external;

  function setAssetAllocations(bytes32[] calldata symbols, uint256[] calldata weights) external;

  function removeCollateralAsset(address collateralAsset, uint256 index) external returns (bool);

  function addOtherReserveAddress(address otherReserveAddress) external returns (bool);

  function removeOtherReserveAddress(address otherReserveAddress, uint256 index) external returns (bool);

  function collateralAssets(uint256 index) external view returns (address);

  function collateralAssetLastSpendingDay(address collateralAsset) external view returns (uint256);
}

File 9 of 15 : ITradingLimits.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >0.5.13 <0.9;

interface ITradingLimits {
  /**
   * @dev The State struct contains the current state of a trading limit config.
   * @param lastUpdated0 The timestamp of the last reset of netflow0.
   * @param lastUpdated1 The timestamp of the last reset of netflow1.
   * @param netflow0 The current netflow of the asset for limit0.
   * @param netflow1 The current netflow of the asset for limit1.
   * @param netflowGlobal The current netflow of the asset for limitGlobal.
   */
  struct State {
    uint32 lastUpdated0;
    uint32 lastUpdated1;
    int48 netflow0;
    int48 netflow1;
    int48 netflowGlobal;
  }

  /**
   * @dev The Config struct contains the configuration of trading limits.
   * @param timestep0 The time window in seconds for limit0.
   * @param timestep1 The time window in seconds for limit1.
   * @param limit0 The limit0 for the asset.
   * @param limit1 The limit1 for the asset.
   * @param limitGlobal The global limit for the asset.
   * @param flags A bitfield of flags to enable/disable the individual limits.
   */
  struct Config {
    uint32 timestep0;
    uint32 timestep1;
    int48 limit0;
    int48 limit1;
    int48 limitGlobal;
    uint8 flags;
  }
}

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

import { ITradingLimits } from "contracts/interfaces/ITradingLimits.sol";

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

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

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

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

  /**
   * @notice  Updates a trading limit State in the context of a Config with the deltaFlow provided.
   * @dev Reverts if the values provided cause overflows.
   * @param self the trading limit State to update.
   * @param config the trading limit Config for the provided State.
   * @param _deltaFlow the delta flow to add to the netflow.
   * @param decimals the number of decimals the _deltaFlow is denominated in.
   * @return State the updated state.
   */
  function update(
    ITradingLimits.State memory self,
    ITradingLimits.Config memory config,
    int256 _deltaFlow,
    uint8 decimals
  ) internal view returns (ITradingLimits.State memory) {
    if (_deltaFlow == 0) {
      return self;
    }

    int256 _deltaFlowUnits = _deltaFlow / int256((10 ** uint256(decimals)));
    require(_deltaFlowUnits <= MAX_INT48, "dFlow too large");
    require(_deltaFlowUnits >= MIN_INT48, "dFlow too small");

    int48 deltaFlowUnits = int48(_deltaFlowUnits);
    if (deltaFlowUnits == 0) {
      deltaFlowUnits = _deltaFlow > 0 ? int48(1) : int48(-1);
    }

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

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

    return self;
  }

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

File 11 of 15 : Initializable.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

contract Initializable {
  bool public initialized;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.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 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.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

Settings
{
  "remappings": [
    "openzeppelin-solidity/=lib/openzeppelin-contracts/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts-next/",
    "openzeppelin-contracts-next/=lib/openzeppelin-contracts-next/",
    "test/=test/",
    "forge-std/=lib/forge-std/src/",
    "safe-contracts/=lib/safe-contracts/",
    "prb/math/=lib/prb-math/src/",
    "celo/=node_modules/@celo/",
    "contracts/=contracts/",
    "@chainlink/contracts/=lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/contracts/src/",
    "@openzeppelin/=lib/foundry-chainlink-toolkit/lib/openzeppelin-contracts/",
    "@prb/test/=lib/prb-math/lib/prb-test/src/",
    "chainlink-brownie-contracts/=lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/@arbitrum/nitro-contracts/src/",
    "ds-test/=lib/prb-math/lib/forge-std/lib/ds-test/src/",
    "foundry-chainlink-toolkit/=lib/foundry-chainlink-toolkit/",
    "mento-std/=lib/mento-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-math/lib/prb-test/src/",
    "src/=lib/prb-math/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"bool","name":"test","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exchangeProvider","type":"address"}],"name":"ExchangeProviderAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exchangeProvider","type":"address"}],"name":"ExchangeProviderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddress","type":"address"},{"indexed":true,"internalType":"address","name":"prevAddress","type":"address"}],"name":"ReserveSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"exchangeProvider","type":"address"},{"indexed":true,"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"timestep0","type":"uint32"},{"internalType":"uint32","name":"timestep1","type":"uint32"},{"internalType":"int48","name":"limit0","type":"int48"},{"internalType":"int48","name":"limit1","type":"int48"},{"internalType":"int48","name":"limitGlobal","type":"int48"},{"internalType":"uint8","name":"flags","type":"uint8"}],"indexed":false,"internalType":"struct ITradingLimits.Config","name":"config","type":"tuple"}],"name":"TradingLimitConfigured","type":"event"},{"inputs":[],"name":"__deprecated0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"address","name":"reserve","type":"address"}],"name":"addExchangeProvider","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnStableTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"timestep0","type":"uint32"},{"internalType":"uint32","name":"timestep1","type":"uint32"},{"internalType":"int48","name":"limit0","type":"int48"},{"internalType":"int48","name":"limit1","type":"int48"},{"internalType":"int48","name":"limitGlobal","type":"int48"},{"internalType":"uint8","name":"flags","type":"uint8"}],"internalType":"struct ITradingLimits.Config","name":"config","type":"tuple"}],"name":"configureTradingLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"exchangeProviders","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"exchangeReserve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeProviders","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_exchangeProviders","type":"address[]"},{"internalType":"address[]","name":"_reserves","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isExchangeProvider","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeExchangeProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_exchangeProviders","type":"address[]"},{"internalType":"address[]","name":"_reserves","type":"address[]"}],"name":"setReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapIn","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"exchangeProvider","type":"address"},{"internalType":"bytes32","name":"exchangeId","type":"bytes32"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"}],"name":"swapOut","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"tradingLimitsConfig","outputs":[{"internalType":"uint32","name":"timestep0","type":"uint32"},{"internalType":"uint32","name":"timestep1","type":"uint32"},{"internalType":"int48","name":"limit0","type":"int48"},{"internalType":"int48","name":"limit1","type":"int48"},{"internalType":"int48","name":"limitGlobal","type":"int48"},{"internalType":"uint8","name":"flags","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"tradingLimitsState","outputs":[{"internalType":"uint32","name":"lastUpdated0","type":"uint32"},{"internalType":"uint32","name":"lastUpdated1","type":"uint32"},{"internalType":"int48","name":"netflow0","type":"int48"},{"internalType":"int48","name":"netflow1","type":"int48"},{"internalType":"int48","name":"netflowGlobal","type":"int48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162003157380380620031578339810160408190526200003491620000b9565b808062000049576000805460ff191660011790555b50620000553362000060565b5060018055620000e4565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b600060208284031215620000cc57600080fd5b81518015158114620000dd57600080fd5b9392505050565b61306380620000f46000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c806387071a2d116100b8578063d163b1351161007c578063d163b13514610326578063d1d786b114610339578063ddbbe8501461035c578063ddeb9dd21461036f578063f01ecd1714610382578063f2fde38b1461041057600080fd5b806387071a2d146102965780638da5cb5b146102d7578063a20f2305146102ed578063a9b5aab314610300578063c4454fdc1461031357600080fd5b80635a2248b2116100ff5780635a2248b2146101bc578063715018a6146101c557806373cf25f8146101cd578063770d0a34146101e0578063821a816c146101f357600080fd5b806304710d531461013c57806304e4564014610151578063131cab2a14610177578063158ef93e1461019a5780632cac2568146101a7575b600080fd5b61014f61014a36600461299c565b610423565b005b61016461015f3660046129c6565b6105bb565b6040519081526020015b60405180910390f35b61018a61018536600461299c565b6107b0565b604051901515815260200161016e565b60005461018a9060ff1681565b6101af6107e4565b60405161016e9190612a1b565b61016460065481565b61014f610846565b61014f6101db366004612ab4565b61085a565b6101646101ee366004612b20565b610939565b610254610201366004612b53565b60056020819052600091825260409091205463ffffffff80821692640100000000830490911691600160401b8104820b91600160701b8204810b91600160a01b810490910b90600160d01b900460ff1686565b6040805163ffffffff9788168152969095166020870152600593840b9486019490945290820b6060850152900b608083015260ff1660a082015260c00161016e565b6102bf6102a4366004612b6c565b6007602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161016e565b60005461010090046001600160a01b03166102bf565b6101646102fb3660046129c6565b610b74565b61014f61030e366004612bc7565b610d6a565b6102bf610321366004612b53565b6110ad565b610164610334366004612ca7565b6110d7565b61018a610347366004612b6c565b60036020526000908152604090205460ff1681565b61016461036a366004612ca7565b611287565b61014f61037d366004612ab4565b61141f565b6103d8610390366004612b53565b60046020526000908152604090205463ffffffff80821691640100000000810490911690600160401b8104600590810b91600160701b8104820b91600160a01b909104900b85565b6040805163ffffffff9687168152959094166020860152600592830b93850193909352810b60608401520b608082015260a00161016e565b61014f61041e366004612b6c565b611636565b61042b6116af565b816001600160a01b03166002828154811061044857610448612d06565b6000918252602090912001546001600160a01b0316146104af5760405162461bcd60e51b815260206004820152601c60248201527f696e64657820646f65736e2774206d617463682070726f76696465720000000060448201526064015b60405180910390fd5b600280546104bf90600190612d32565b815481106104cf576104cf612d06565b600091825260209091200154600280546001600160a01b0390921691839081106104fb576104fb612d06565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600280548061053a5761053a612d45565b60008281526020808220830160001990810180546001600160a01b031990811690915593019093556001600160a01b038516808252600384526040808320805460ff1916905560079094528382208054909316909255915190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b6001600160a01b03851660009081526003602052604081205460ff166105f35760405162461bcd60e51b81526004016104a690612d5b565b6001600160a01b0386811660009081526007602052604090819020549051636570c17f60e11b81528583166004820152911690819063cae182fe90602401602060405180830381865afa15801561064e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106729190612d92565b15610732576040516370a0823160e01b81526001600160a01b0382811660048301528491908616906370a0823190602401602060405180830381865afa1580156106c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e49190612db4565b10156107325760405162461bcd60e51b815260206004820152601f60248201527f496e73756666696369656e742062616c616e636520696e20726573657276650060448201526064016104a6565b60405163f670dde160e01b81526001600160a01b0388169063f670dde190610764908990899089908990600401612dcd565b602060405180830381865afa158015610781573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a59190612db4565b979650505050505050565b60006107c76001600160a01b03841633308561170f565b6107da6001600160a01b03841683611780565b5060015b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561083c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161081e575b5050505050905090565b61084e6116af565b61085860006117a3565b565b60005460ff16156108ad5760405162461bcd60e51b815260206004820152601c60248201527f636f6e747261637420616c726561647920696e697469616c697a65640000000060448201526064016104a6565b6000805460ff191660011790556108c3336117a3565b60005b838110156109325761091f8585838181106108e3576108e3612d06565b90506020020160208101906108f89190612b6c565b84848481811061090a5761090a612d06565b90506020020160208101906101ee9190612b6c565b508061092a81612df2565b9150506108c6565b5050505050565b60006109436116af565b6001600160a01b03831660009081526003602052604090205460ff16156109c05760405162461bcd60e51b815260206004820152602b60248201527f45786368616e676550726f766964657220616c7265616479206578697374732060448201526a1a5b881d1a19481b1a5cdd60aa1b60648201526084016104a6565b6001600160a01b038316610a225760405162461bcd60e51b815260206004820152602360248201527f45786368616e676550726f766964657220616464726573732063616e2774206260448201526206520360ec1b60648201526084016104a6565b6001600160a01b038216610a785760405162461bcd60e51b815260206004820152601a60248201527f5265736572766520616464726573732063616e2774206265203000000000000060448201526064016104a6565b6002805460018082019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b03199081166001600160a01b038781169182179093556000818152600360209081526040808320805460ff19169097179096556007905284812080549093169387169390931790915591517f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de29190a2816001600160a01b0316836001600160a01b03167fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5060405160405180910390a3600254610b6d90600190612d32565b9392505050565b6001600160a01b03851660009081526003602052604081205460ff16610bac5760405162461bcd60e51b81526004016104a690612d5b565b6040516324f1f8ef60e21b81526001600160a01b038716906393c7e3bc90610bde908890889088908890600401612dcd565b602060405180830381865afa158015610bfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1f9190612db4565b6001600160a01b0387811660009081526007602052604090819020549051636570c17f60e11b815286831660048201529293501690819063cae182fe90602401602060405180830381865afa158015610c7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca09190612d92565b15610d60576040516370a0823160e01b81526001600160a01b0382811660048301528391908616906370a0823190602401602060405180830381865afa158015610cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d129190612db4565b1015610d605760405162461bcd60e51b815260206004820152601f60248201527f496e73756666696369656e742062616c616e636520696e20726573657276650060448201526064016104a6565b5095945050505050565b610d726116af565b610d7b816117fc565b6000826001600160a01b031660001b84189050816005600083815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548165ffffffffffff021916908360050b65ffffffffffff160217905550606082015181600001600e6101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060808201518160000160146101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060a082015181600001601a6101000a81548160ff021916908360ff160217905550905050610f7182600460008481526020019081526020016000206040518060a00160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160089054906101000a900460050b60050b60050b815260200160008201600e9054906101000a900460050b60050b60050b81526020016000820160149054906101000a900460050b60050b60050b81525050611c0290919063ffffffff16565b6000828152600460209081526040918290208351815485840151868601516060808901516080998a015163ffffffff96871667ffffffffffffffff199096169590951764010000000094871694909402939093176bffffffffffffffffffffffff60401b1916600160401b65ffffffffffff9384160265ffffffffffff60701b191617600160701b938316939093029290921765ffffffffffff60a01b1916600160a01b91909316029190911790925583518981526001600160a01b03891681850152875182168186015292870151168282015291850151600590810b8285015291850151820b60a0808301919091529285015190910b60c08201529083015160ff1660e08201527f1a082a1efaf1549c18917ddcb1f759680c29371609e24f4b946e93acf4ce5ad2906101000160405180910390a150505050565b600281815481106110bd57600080fd5b6000918252602090912001546001600160a01b0316905081565b60006110e1611c84565b6001600160a01b03871660009081526003602052604090205460ff166111195760405162461bcd60e51b81526004016104a690612d5b565b60405163d3385d0560e01b81526001600160a01b0388169063d3385d059061114b908990899089908990600401612dcd565b6020604051808303816000875af115801561116a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118e9190612db4565b9050818111156111d75760405162461bcd60e51b8152602060048201526014602482015273185b5bdd5b9d125b93585e08195e18d95959195960621b60448201526064016104a6565b6111e48686838787611cdd565b6001600160a01b038088166000908152600760205260409020541661120b33878484611db2565b61121733868684611f2f565b604080516001600160a01b038a8116825287811660208301529181018490526060810186905290871690339089907fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f906080015b60405180910390a45061127d60018055565b9695505050505050565b6000611291611c84565b6001600160a01b03871660009081526003602052604090205460ff166112c95760405162461bcd60e51b81526004016104a690612d5b565b6040516310aff26760e21b81526001600160a01b038816906342bfc99c906112fb908990899089908990600401612dcd565b6020604051808303816000875af115801561131a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133e9190612db4565b9050818110156113875760405162461bcd60e51b8152602060048201526014602482015273185b5bdd5b9d13dd5d135a5b881b9bdd081b595d60621b60448201526064016104a6565b6113948686858785611cdd565b6001600160a01b03808816600090815260076020526040902054166113bb33878684611db2565b6113c733868484611f2f565b604080516001600160a01b038a8116825287811660208301529181018690526060810184905290871690339089907fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f9060800161126b565b6114276116af565b60005b83811015610932576003600086868481811061144857611448612d06565b905060200201602081019061145d9190612b6c565b6001600160a01b0316815260208101919091526040016000205460ff166114965760405162461bcd60e51b81526004016104a690612d5b565b60008383838181106114aa576114aa612d06565b90506020020160208101906114bf9190612b6c565b6001600160a01b0316036115155760405162461bcd60e51b815260206004820152601a60248201527f5265736572766520616464726573732063616e2774206265203000000000000060448201526064016104a6565b82828281811061152757611527612d06565b905060200201602081019061153c9190612b6c565b6007600087878581811061155257611552612d06565b90506020020160208101906115679190612b6c565b6001600160a01b039081168252602082019290925260400160002080546001600160a01b031916929091169190911790558282828181106115aa576115aa612d06565b90506020020160208101906115bf9190612b6c565b6001600160a01b03168585838181106115da576115da612d06565b90506020020160208101906115ef9190612b6c565b6001600160a01b03167fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5060405160405180910390a38061162e81612df2565b91505061142a565b61163e6116af565b6001600160a01b0381166116a35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016104a6565b6116ac816117a3565b50565b6000546001600160a01b036101009091041633146108585760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104a6565b6040516001600160a01b038085166024830152831660448201526064810182905261177a9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526120fb565b50505050565b61179f826342966c6860e01b8360405160240161174391815260200190565b5050565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b60a08101516002161580611816575060a081015160011615155b6118625760405162461bcd60e51b815260206004820152601960248201527f4c3120776974686f7574204c30206e6f7420616c6c6f7765640000000000000060448201526064016104a6565b60a0810151600116158061187c5750805163ffffffff1615155b6118d25760405162461bcd60e51b815260206004820152602160248201527f74696d6573746570302063616e2774206265207a65726f2069662061637469766044820152606560f81b60648201526084016104a6565b60a081015160021615806118f057506000816020015163ffffffff16115b6119465760405162461bcd60e51b815260206004820152602160248201527f74696d6573746570312063616e2774206265207a65726f2069662061637469766044820152606560f81b60648201526084016104a6565b60a0810151600116158061196157506000816040015160050b135b6119ad5760405162461bcd60e51b815260206004820152601e60248201527f6c696d6974302063616e2774206265207a65726f20696620616374697665000060448201526064016104a6565b60a081015160021615806119c857506000816060015160050b135b611a145760405162461bcd60e51b815260206004820152601e60248201527f6c696d6974312063616e2774206265207a65726f20696620616374697665000060448201526064016104a6565b60a08101516004161580611a2f57506000816080015160050b135b611a875760405162461bcd60e51b815260206004820152602360248201527f6c696d6974476c6f62616c2063616e2774206265207a65726f2069662061637460448201526269766560e81b60648201526084016104a6565b60a08101516003908116141580611aab5750806060015160050b816040015160050b125b611b025760405162461bcd60e51b815260206004820152602260248201527f6c696d697431206d7573742062652067726561746572207468616e206c696d69604482015261074360f41b60648201526084016104a6565b60a08101516006908116141580611b265750806080015160050b816060015160050b125b611b825760405162461bcd60e51b815260206004820152602760248201527f6c696d6974476c6f62616c206d7573742062652067726561746572207468616e604482015266206c696d69743160c81b60648201526084016104a6565b60a08101516005908116141580611ba65750806080015160050b816040015160050b125b6116ac5760405162461bcd60e51b815260206004820152602760248201527f6c696d6974476c6f62616c206d7573742062652067726561746572207468616e6044820152660206c696d6974360cc1b60648201526084016104a6565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915260008084526020840181905260a08301516001169003611c4f57600060408401525b60a0820151600216600003611c6657600060608401525b60a0820151600416600003611c7d57600060808401525b5090919050565b600260015403611cd65760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104a6565b6002600155565b6001600160a01b038085169083166001600160ff1b03851115611d375760405162461bcd60e51b8152602060048201526012602482015271616d6f756e74496e20746f6f206c6172676560701b60448201526064016104a6565b6001600160ff1b03831115611d845760405162461bcd60e51b8152602060048201526013602482015272616d6f756e744f757420746f6f206c6172676560681b60448201526064016104a6565b611d9182881886886121d2565b611da9878218611da385600019612e0b565b866121d2565b50505050505050565b604051634f8e6e2360e01b81526001600160a01b038481166004830152829190821690634f8e6e2390602401602060405180830381865afa158015611dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1f9190612d92565b15611e5157611e396001600160a01b03851686308661170f565b611e4c6001600160a01b03851684611780565b610932565b604051636570c17f60e11b81526001600160a01b03858116600483015282169063cae182fe90602401602060405180830381865afa158015611e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ebb9190612d92565b15611ed557611e4c6001600160a01b03851686838661170f565b60405162461bcd60e51b815260206004820152602960248201527f546f6b656e206d75737420626520737461626c65206f7220636f6c6c61746572604482015268185b08185cdcd95c9d60ba1b60648201526084016104a6565b604051634f8e6e2360e01b81526001600160a01b038481166004830152829190821690634f8e6e2390602401602060405180830381865afa158015611f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9c9190612d92565b15611fb557611e4c6001600160a01b03851686856123e4565b604051636570c17f60e11b81526001600160a01b03858116600483015282169063cae182fe90602401602060405180830381865afa158015611ffb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201f9190612d92565b15611ed557604051631af8e0ff60e21b81526001600160a01b038581166004830152868116602483015260448201859052821690636be383fc906064016020604051808303816000875af115801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061209f9190612d92565b611e4c5760405162461bcd60e51b815260206004820152602760248201527f5472616e73666572206f662074686520636f6c6c61746572616c2061737365746044820152660819985a5b195960ca1b60648201526084016104a6565b6000612150826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124149092919063ffffffff16565b8051909150156121cd578080602001905181019061216e9190612d92565b6121cd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016104a6565b505050565b600083815260056020818152604092839020835160c081018552905463ffffffff808216835264010000000082041692820192909252600160401b8204830b93810193909352600160701b8104820b6060840152600160a01b810490910b6080830152600160d01b900460ff1660a082018190521561177a57600084815260046020818152604092839020835160a081018552905463ffffffff808216835264010000000082041682840152600160401b8104600590810b83870152600160701b8204810b6060840152600160a01b909104900b6080820152835163313ce56760e01b81529351909361232093869389936001600160a01b038a169363313ce567938181019392918290030181865afa1580156122f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123179190612e3b565b8492919061242b565b905061232c818361260c565b600094855260046020908152604095869020825181549284015197840151606085015160809095015163ffffffff92831667ffffffffffffffff19909516949094176401000000009290991691909102979097176bffffffffffffffffffffffff60401b1916600160401b65ffffffffffff9889160265ffffffffffff60701b191617600160701b938816939093029290921765ffffffffffff60a01b1916600160a01b969091169590950294909417909355505050565b6040516001600160a01b0383166024820152604481018290526121cd9084906340c10f1960e01b90606401611743565b60606124238484600085612792565b949350505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915282600003612465575083612423565b600061247560ff8416600a612f3c565b61247f9085612f48565b9050657fffffffffff8113156124c95760405162461bcd60e51b815260206004820152600f60248201526e64466c6f7720746f6f206c6172676560881b60448201526064016104a6565b657fffffffffff198112156125125760405162461bcd60e51b815260206004820152600f60248201526e19119b1bddc81d1bdbc81cdb585b1b608a1b60448201526064016104a6565b80600581900b600003612535576000851361252f57600019612532565b60015b90505b60a0860151600116156125dd57855187516125509190612f84565b63ffffffff1642111561256e576000604088015263ffffffff421687525b61257c876040015182612862565b60050b604088015260a0860151600216156125dd57856020015187602001516125a59190612f84565b63ffffffff164211156125c6576000606088015263ffffffff421660208801525b6125d4876060015182612862565b60050b60608801525b60a086015160041615612601576125f8876080015182612862565b60050b60808801525b509495945050505050565b60a0810151600116158015906126535750816040015160050b81604001516000196126379190612fa8565b60050b13806126535750806040015160050b826040015160050b135b1561268e5760405162461bcd60e51b815260206004820152600b60248201526a130c08115e18d95959195960aa1b60448201526064016104a6565b60a0810151600216158015906126d55750816060015160050b81606001516000196126b99190612fa8565b60050b13806126d55750806060015160050b826060015160050b135b156127105760405162461bcd60e51b815260206004820152600b60248201526a130c48115e18d95959195960aa1b60448201526064016104a6565b60a0810151600416158015906127575750816080015160050b816080015160001961273b9190612fa8565b60050b13806127575750806080015160050b826080015160050b135b1561179f5760405162461bcd60e51b815260206004820152600b60248201526a1311c8115e18d95959195960aa1b60448201526064016104a6565b6060824710156127f35760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104a6565b600080866001600160a01b0316858760405161280f9190612fec565b60006040518083038185875af1925050503d806000811461284c576040519150601f19603f3d011682016040523d82523d6000602084013e612851565b606091505b50915091506107a5878383876128e2565b6000808260050b8460050b6128779190613008565b9050657fffffffffff1981128015906128965750657fffffffffff8113155b610b6d5760405162461bcd60e51b815260206004820152601760248201527f696e743438206164646974696f6e206f766572666c6f7700000000000000000060448201526064016104a6565b6060831561295157825160000361294a576001600160a01b0385163b61294a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104a6565b5081612423565b61242383838151156129665781518083602001fd5b8060405162461bcd60e51b81526004016104a69190613030565b80356001600160a01b038116811461299757600080fd5b919050565b600080604083850312156129af57600080fd5b6129b883612980565b946020939093013593505050565b600080600080600060a086880312156129de57600080fd5b6129e786612980565b9450602086013593506129fc60408701612980565b9250612a0a60608701612980565b949793965091946080013592915050565b6020808252825182820181905260009190848201906040850190845b81811015612a5c5783516001600160a01b031683529284019291840191600101612a37565b50909695505050505050565b60008083601f840112612a7a57600080fd5b50813567ffffffffffffffff811115612a9257600080fd5b6020830191508360208260051b8501011115612aad57600080fd5b9250929050565b60008060008060408587031215612aca57600080fd5b843567ffffffffffffffff80821115612ae257600080fd5b612aee88838901612a68565b90965094506020870135915080821115612b0757600080fd5b50612b1487828801612a68565b95989497509550505050565b60008060408385031215612b3357600080fd5b612b3c83612980565b9150612b4a60208401612980565b90509250929050565b600060208284031215612b6557600080fd5b5035919050565b600060208284031215612b7e57600080fd5b610b6d82612980565b803563ffffffff8116811461299757600080fd5b8035600581900b811461299757600080fd5b60ff811681146116ac57600080fd5b803561299781612bad565b6000806000838503610100811215612bde57600080fd5b84359350612bee60208601612980565b925060c0603f1982011215612c0257600080fd5b5060405160c0810181811067ffffffffffffffff82111715612c3457634e487b7160e01b600052604160045260246000fd5b8060405250612c4560408601612b87565b8152612c5360608601612b87565b6020820152612c6460808601612b9b565b6040820152612c7560a08601612b9b565b6060820152612c8660c08601612b9b565b6080820152612c9760e08601612bbc565b60a0820152809150509250925092565b60008060008060008060c08789031215612cc057600080fd5b612cc987612980565b955060208701359450612cde60408801612980565b9350612cec60608801612980565b92506080870135915060a087013590509295509295509295565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156107de576107de612d1c565b634e487b7160e01b600052603160045260246000fd5b6020808252601f908201527f45786368616e676550726f766964657220646f6573206e6f7420657869737400604082015260600190565b600060208284031215612da457600080fd5b81518015158114610b6d57600080fd5b600060208284031215612dc657600080fd5b5051919050565b9384526001600160a01b03928316602085015291166040830152606082015260800190565b600060018201612e0457612e04612d1c565b5060010190565b80820260008212600160ff1b84141615612e2757612e27612d1c565b81810583148215176107de576107de612d1c565b600060208284031215612e4d57600080fd5b8151610b6d81612bad565b600181815b80851115612e93578160001904821115612e7957612e79612d1c565b80851615612e8657918102915b93841c9390800290612e5d565b509250929050565b600082612eaa575060016107de565b81612eb7575060006107de565b8160018114612ecd5760028114612ed757612ef3565b60019150506107de565b60ff841115612ee857612ee8612d1c565b50506001821b6107de565b5060208310610133831016604e8410600b8410161715612f16575081810a6107de565b612f208383612e58565b8060001904821115612f3457612f34612d1c565b029392505050565b6000610b6d8383612e9b565b600082612f6557634e487b7160e01b600052601260045260246000fd5b600160ff1b821460001984141615612f7f57612f7f612d1c565b500590565b63ffffffff818116838216019080821115612fa157612fa1612d1c565b5092915050565b60008260050b8260050b028060050b9150808214612fa157612fa1612d1c565b60005b83811015612fe3578181015183820152602001612fcb565b50506000910152565b60008251612ffe818460208701612fc8565b9190910192915050565b808201828112600083128015821682158216171561302857613028612d1c565b505092915050565b602081526000825180602084015261304f816040850160208701612fc8565b601f01601f19169190910160400192915050560000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101375760003560e01c806387071a2d116100b8578063d163b1351161007c578063d163b13514610326578063d1d786b114610339578063ddbbe8501461035c578063ddeb9dd21461036f578063f01ecd1714610382578063f2fde38b1461041057600080fd5b806387071a2d146102965780638da5cb5b146102d7578063a20f2305146102ed578063a9b5aab314610300578063c4454fdc1461031357600080fd5b80635a2248b2116100ff5780635a2248b2146101bc578063715018a6146101c557806373cf25f8146101cd578063770d0a34146101e0578063821a816c146101f357600080fd5b806304710d531461013c57806304e4564014610151578063131cab2a14610177578063158ef93e1461019a5780632cac2568146101a7575b600080fd5b61014f61014a36600461299c565b610423565b005b61016461015f3660046129c6565b6105bb565b6040519081526020015b60405180910390f35b61018a61018536600461299c565b6107b0565b604051901515815260200161016e565b60005461018a9060ff1681565b6101af6107e4565b60405161016e9190612a1b565b61016460065481565b61014f610846565b61014f6101db366004612ab4565b61085a565b6101646101ee366004612b20565b610939565b610254610201366004612b53565b60056020819052600091825260409091205463ffffffff80821692640100000000830490911691600160401b8104820b91600160701b8204810b91600160a01b810490910b90600160d01b900460ff1686565b6040805163ffffffff9788168152969095166020870152600593840b9486019490945290820b6060850152900b608083015260ff1660a082015260c00161016e565b6102bf6102a4366004612b6c565b6007602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161016e565b60005461010090046001600160a01b03166102bf565b6101646102fb3660046129c6565b610b74565b61014f61030e366004612bc7565b610d6a565b6102bf610321366004612b53565b6110ad565b610164610334366004612ca7565b6110d7565b61018a610347366004612b6c565b60036020526000908152604090205460ff1681565b61016461036a366004612ca7565b611287565b61014f61037d366004612ab4565b61141f565b6103d8610390366004612b53565b60046020526000908152604090205463ffffffff80821691640100000000810490911690600160401b8104600590810b91600160701b8104820b91600160a01b909104900b85565b6040805163ffffffff9687168152959094166020860152600592830b93850193909352810b60608401520b608082015260a00161016e565b61014f61041e366004612b6c565b611636565b61042b6116af565b816001600160a01b03166002828154811061044857610448612d06565b6000918252602090912001546001600160a01b0316146104af5760405162461bcd60e51b815260206004820152601c60248201527f696e64657820646f65736e2774206d617463682070726f76696465720000000060448201526064015b60405180910390fd5b600280546104bf90600190612d32565b815481106104cf576104cf612d06565b600091825260209091200154600280546001600160a01b0390921691839081106104fb576104fb612d06565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600280548061053a5761053a612d45565b60008281526020808220830160001990810180546001600160a01b031990811690915593019093556001600160a01b038516808252600384526040808320805460ff1916905560079094528382208054909316909255915190917f29e92ab2e30f4f74283034c28c451b6faac986b554f1808101eb6418bdba19d491a25050565b6001600160a01b03851660009081526003602052604081205460ff166105f35760405162461bcd60e51b81526004016104a690612d5b565b6001600160a01b0386811660009081526007602052604090819020549051636570c17f60e11b81528583166004820152911690819063cae182fe90602401602060405180830381865afa15801561064e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106729190612d92565b15610732576040516370a0823160e01b81526001600160a01b0382811660048301528491908616906370a0823190602401602060405180830381865afa1580156106c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e49190612db4565b10156107325760405162461bcd60e51b815260206004820152601f60248201527f496e73756666696369656e742062616c616e636520696e20726573657276650060448201526064016104a6565b60405163f670dde160e01b81526001600160a01b0388169063f670dde190610764908990899089908990600401612dcd565b602060405180830381865afa158015610781573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a59190612db4565b979650505050505050565b60006107c76001600160a01b03841633308561170f565b6107da6001600160a01b03841683611780565b5060015b92915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561083c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161081e575b5050505050905090565b61084e6116af565b61085860006117a3565b565b60005460ff16156108ad5760405162461bcd60e51b815260206004820152601c60248201527f636f6e747261637420616c726561647920696e697469616c697a65640000000060448201526064016104a6565b6000805460ff191660011790556108c3336117a3565b60005b838110156109325761091f8585838181106108e3576108e3612d06565b90506020020160208101906108f89190612b6c565b84848481811061090a5761090a612d06565b90506020020160208101906101ee9190612b6c565b508061092a81612df2565b9150506108c6565b5050505050565b60006109436116af565b6001600160a01b03831660009081526003602052604090205460ff16156109c05760405162461bcd60e51b815260206004820152602b60248201527f45786368616e676550726f766964657220616c7265616479206578697374732060448201526a1a5b881d1a19481b1a5cdd60aa1b60648201526084016104a6565b6001600160a01b038316610a225760405162461bcd60e51b815260206004820152602360248201527f45786368616e676550726f766964657220616464726573732063616e2774206260448201526206520360ec1b60648201526084016104a6565b6001600160a01b038216610a785760405162461bcd60e51b815260206004820152601a60248201527f5265736572766520616464726573732063616e2774206265203000000000000060448201526064016104a6565b6002805460018082019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b03199081166001600160a01b038781169182179093556000818152600360209081526040808320805460ff19169097179096556007905284812080549093169387169390931790915591517f2ee2cb0721ec60b86190cae5c48e25064b69b35abad32452a4ec99d232033de29190a2816001600160a01b0316836001600160a01b03167fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5060405160405180910390a3600254610b6d90600190612d32565b9392505050565b6001600160a01b03851660009081526003602052604081205460ff16610bac5760405162461bcd60e51b81526004016104a690612d5b565b6040516324f1f8ef60e21b81526001600160a01b038716906393c7e3bc90610bde908890889088908890600401612dcd565b602060405180830381865afa158015610bfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1f9190612db4565b6001600160a01b0387811660009081526007602052604090819020549051636570c17f60e11b815286831660048201529293501690819063cae182fe90602401602060405180830381865afa158015610c7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca09190612d92565b15610d60576040516370a0823160e01b81526001600160a01b0382811660048301528391908616906370a0823190602401602060405180830381865afa158015610cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d129190612db4565b1015610d605760405162461bcd60e51b815260206004820152601f60248201527f496e73756666696369656e742062616c616e636520696e20726573657276650060448201526064016104a6565b5095945050505050565b610d726116af565b610d7b816117fc565b6000826001600160a01b031660001b84189050816005600083815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548165ffffffffffff021916908360050b65ffffffffffff160217905550606082015181600001600e6101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060808201518160000160146101000a81548165ffffffffffff021916908360050b65ffffffffffff16021790555060a082015181600001601a6101000a81548160ff021916908360ff160217905550905050610f7182600460008481526020019081526020016000206040518060a00160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160089054906101000a900460050b60050b60050b815260200160008201600e9054906101000a900460050b60050b60050b81526020016000820160149054906101000a900460050b60050b60050b81525050611c0290919063ffffffff16565b6000828152600460209081526040918290208351815485840151868601516060808901516080998a015163ffffffff96871667ffffffffffffffff199096169590951764010000000094871694909402939093176bffffffffffffffffffffffff60401b1916600160401b65ffffffffffff9384160265ffffffffffff60701b191617600160701b938316939093029290921765ffffffffffff60a01b1916600160a01b91909316029190911790925583518981526001600160a01b03891681850152875182168186015292870151168282015291850151600590810b8285015291850151820b60a0808301919091529285015190910b60c08201529083015160ff1660e08201527f1a082a1efaf1549c18917ddcb1f759680c29371609e24f4b946e93acf4ce5ad2906101000160405180910390a150505050565b600281815481106110bd57600080fd5b6000918252602090912001546001600160a01b0316905081565b60006110e1611c84565b6001600160a01b03871660009081526003602052604090205460ff166111195760405162461bcd60e51b81526004016104a690612d5b565b60405163d3385d0560e01b81526001600160a01b0388169063d3385d059061114b908990899089908990600401612dcd565b6020604051808303816000875af115801561116a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118e9190612db4565b9050818111156111d75760405162461bcd60e51b8152602060048201526014602482015273185b5bdd5b9d125b93585e08195e18d95959195960621b60448201526064016104a6565b6111e48686838787611cdd565b6001600160a01b038088166000908152600760205260409020541661120b33878484611db2565b61121733868684611f2f565b604080516001600160a01b038a8116825287811660208301529181018490526060810186905290871690339089907fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f906080015b60405180910390a45061127d60018055565b9695505050505050565b6000611291611c84565b6001600160a01b03871660009081526003602052604090205460ff166112c95760405162461bcd60e51b81526004016104a690612d5b565b6040516310aff26760e21b81526001600160a01b038816906342bfc99c906112fb908990899089908990600401612dcd565b6020604051808303816000875af115801561131a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133e9190612db4565b9050818110156113875760405162461bcd60e51b8152602060048201526014602482015273185b5bdd5b9d13dd5d135a5b881b9bdd081b595d60621b60448201526064016104a6565b6113948686858785611cdd565b6001600160a01b03808816600090815260076020526040902054166113bb33878684611db2565b6113c733868484611f2f565b604080516001600160a01b038a8116825287811660208301529181018690526060810184905290871690339089907fe7b046415cac9de47940c3087e06db13a0e058ccf53ac5f0edd49ebb4c2c3a6f9060800161126b565b6114276116af565b60005b83811015610932576003600086868481811061144857611448612d06565b905060200201602081019061145d9190612b6c565b6001600160a01b0316815260208101919091526040016000205460ff166114965760405162461bcd60e51b81526004016104a690612d5b565b60008383838181106114aa576114aa612d06565b90506020020160208101906114bf9190612b6c565b6001600160a01b0316036115155760405162461bcd60e51b815260206004820152601a60248201527f5265736572766520616464726573732063616e2774206265203000000000000060448201526064016104a6565b82828281811061152757611527612d06565b905060200201602081019061153c9190612b6c565b6007600087878581811061155257611552612d06565b90506020020160208101906115679190612b6c565b6001600160a01b039081168252602082019290925260400160002080546001600160a01b031916929091169190911790558282828181106115aa576115aa612d06565b90506020020160208101906115bf9190612b6c565b6001600160a01b03168585838181106115da576115da612d06565b90506020020160208101906115ef9190612b6c565b6001600160a01b03167fb69e1c416d8be92ac92c8e97e77c4626fba5e6ab50161099f659ea3303479e5060405160405180910390a38061162e81612df2565b91505061142a565b61163e6116af565b6001600160a01b0381166116a35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016104a6565b6116ac816117a3565b50565b6000546001600160a01b036101009091041633146108585760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104a6565b6040516001600160a01b038085166024830152831660448201526064810182905261177a9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526120fb565b50505050565b61179f826342966c6860e01b8360405160240161174391815260200190565b5050565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b60a08101516002161580611816575060a081015160011615155b6118625760405162461bcd60e51b815260206004820152601960248201527f4c3120776974686f7574204c30206e6f7420616c6c6f7765640000000000000060448201526064016104a6565b60a0810151600116158061187c5750805163ffffffff1615155b6118d25760405162461bcd60e51b815260206004820152602160248201527f74696d6573746570302063616e2774206265207a65726f2069662061637469766044820152606560f81b60648201526084016104a6565b60a081015160021615806118f057506000816020015163ffffffff16115b6119465760405162461bcd60e51b815260206004820152602160248201527f74696d6573746570312063616e2774206265207a65726f2069662061637469766044820152606560f81b60648201526084016104a6565b60a0810151600116158061196157506000816040015160050b135b6119ad5760405162461bcd60e51b815260206004820152601e60248201527f6c696d6974302063616e2774206265207a65726f20696620616374697665000060448201526064016104a6565b60a081015160021615806119c857506000816060015160050b135b611a145760405162461bcd60e51b815260206004820152601e60248201527f6c696d6974312063616e2774206265207a65726f20696620616374697665000060448201526064016104a6565b60a08101516004161580611a2f57506000816080015160050b135b611a875760405162461bcd60e51b815260206004820152602360248201527f6c696d6974476c6f62616c2063616e2774206265207a65726f2069662061637460448201526269766560e81b60648201526084016104a6565b60a08101516003908116141580611aab5750806060015160050b816040015160050b125b611b025760405162461bcd60e51b815260206004820152602260248201527f6c696d697431206d7573742062652067726561746572207468616e206c696d69604482015261074360f41b60648201526084016104a6565b60a08101516006908116141580611b265750806080015160050b816060015160050b125b611b825760405162461bcd60e51b815260206004820152602760248201527f6c696d6974476c6f62616c206d7573742062652067726561746572207468616e604482015266206c696d69743160c81b60648201526084016104a6565b60a08101516005908116141580611ba65750806080015160050b816040015160050b125b6116ac5760405162461bcd60e51b815260206004820152602760248201527f6c696d6974476c6f62616c206d7573742062652067726561746572207468616e6044820152660206c696d6974360cc1b60648201526084016104a6565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915260008084526020840181905260a08301516001169003611c4f57600060408401525b60a0820151600216600003611c6657600060608401525b60a0820151600416600003611c7d57600060808401525b5090919050565b600260015403611cd65760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104a6565b6002600155565b6001600160a01b038085169083166001600160ff1b03851115611d375760405162461bcd60e51b8152602060048201526012602482015271616d6f756e74496e20746f6f206c6172676560701b60448201526064016104a6565b6001600160ff1b03831115611d845760405162461bcd60e51b8152602060048201526013602482015272616d6f756e744f757420746f6f206c6172676560681b60448201526064016104a6565b611d9182881886886121d2565b611da9878218611da385600019612e0b565b866121d2565b50505050505050565b604051634f8e6e2360e01b81526001600160a01b038481166004830152829190821690634f8e6e2390602401602060405180830381865afa158015611dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1f9190612d92565b15611e5157611e396001600160a01b03851686308661170f565b611e4c6001600160a01b03851684611780565b610932565b604051636570c17f60e11b81526001600160a01b03858116600483015282169063cae182fe90602401602060405180830381865afa158015611e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ebb9190612d92565b15611ed557611e4c6001600160a01b03851686838661170f565b60405162461bcd60e51b815260206004820152602960248201527f546f6b656e206d75737420626520737461626c65206f7220636f6c6c61746572604482015268185b08185cdcd95c9d60ba1b60648201526084016104a6565b604051634f8e6e2360e01b81526001600160a01b038481166004830152829190821690634f8e6e2390602401602060405180830381865afa158015611f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9c9190612d92565b15611fb557611e4c6001600160a01b03851686856123e4565b604051636570c17f60e11b81526001600160a01b03858116600483015282169063cae182fe90602401602060405180830381865afa158015611ffb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201f9190612d92565b15611ed557604051631af8e0ff60e21b81526001600160a01b038581166004830152868116602483015260448201859052821690636be383fc906064016020604051808303816000875af115801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061209f9190612d92565b611e4c5760405162461bcd60e51b815260206004820152602760248201527f5472616e73666572206f662074686520636f6c6c61746572616c2061737365746044820152660819985a5b195960ca1b60648201526084016104a6565b6000612150826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124149092919063ffffffff16565b8051909150156121cd578080602001905181019061216e9190612d92565b6121cd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016104a6565b505050565b600083815260056020818152604092839020835160c081018552905463ffffffff808216835264010000000082041692820192909252600160401b8204830b93810193909352600160701b8104820b6060840152600160a01b810490910b6080830152600160d01b900460ff1660a082018190521561177a57600084815260046020818152604092839020835160a081018552905463ffffffff808216835264010000000082041682840152600160401b8104600590810b83870152600160701b8204810b6060840152600160a01b909104900b6080820152835163313ce56760e01b81529351909361232093869389936001600160a01b038a169363313ce567938181019392918290030181865afa1580156122f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123179190612e3b565b8492919061242b565b905061232c818361260c565b600094855260046020908152604095869020825181549284015197840151606085015160809095015163ffffffff92831667ffffffffffffffff19909516949094176401000000009290991691909102979097176bffffffffffffffffffffffff60401b1916600160401b65ffffffffffff9889160265ffffffffffff60701b191617600160701b938816939093029290921765ffffffffffff60a01b1916600160a01b969091169590950294909417909355505050565b6040516001600160a01b0383166024820152604481018290526121cd9084906340c10f1960e01b90606401611743565b60606124238484600085612792565b949350505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915282600003612465575083612423565b600061247560ff8416600a612f3c565b61247f9085612f48565b9050657fffffffffff8113156124c95760405162461bcd60e51b815260206004820152600f60248201526e64466c6f7720746f6f206c6172676560881b60448201526064016104a6565b657fffffffffff198112156125125760405162461bcd60e51b815260206004820152600f60248201526e19119b1bddc81d1bdbc81cdb585b1b608a1b60448201526064016104a6565b80600581900b600003612535576000851361252f57600019612532565b60015b90505b60a0860151600116156125dd57855187516125509190612f84565b63ffffffff1642111561256e576000604088015263ffffffff421687525b61257c876040015182612862565b60050b604088015260a0860151600216156125dd57856020015187602001516125a59190612f84565b63ffffffff164211156125c6576000606088015263ffffffff421660208801525b6125d4876060015182612862565b60050b60608801525b60a086015160041615612601576125f8876080015182612862565b60050b60808801525b509495945050505050565b60a0810151600116158015906126535750816040015160050b81604001516000196126379190612fa8565b60050b13806126535750806040015160050b826040015160050b135b1561268e5760405162461bcd60e51b815260206004820152600b60248201526a130c08115e18d95959195960aa1b60448201526064016104a6565b60a0810151600216158015906126d55750816060015160050b81606001516000196126b99190612fa8565b60050b13806126d55750806060015160050b826060015160050b135b156127105760405162461bcd60e51b815260206004820152600b60248201526a130c48115e18d95959195960aa1b60448201526064016104a6565b60a0810151600416158015906127575750816080015160050b816080015160001961273b9190612fa8565b60050b13806127575750806080015160050b826080015160050b135b1561179f5760405162461bcd60e51b815260206004820152600b60248201526a1311c8115e18d95959195960aa1b60448201526064016104a6565b6060824710156127f35760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104a6565b600080866001600160a01b0316858760405161280f9190612fec565b60006040518083038185875af1925050503d806000811461284c576040519150601f19603f3d011682016040523d82523d6000602084013e612851565b606091505b50915091506107a5878383876128e2565b6000808260050b8460050b6128779190613008565b9050657fffffffffff1981128015906128965750657fffffffffff8113155b610b6d5760405162461bcd60e51b815260206004820152601760248201527f696e743438206164646974696f6e206f766572666c6f7700000000000000000060448201526064016104a6565b6060831561295157825160000361294a576001600160a01b0385163b61294a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104a6565b5081612423565b61242383838151156129665781518083602001fd5b8060405162461bcd60e51b81526004016104a69190613030565b80356001600160a01b038116811461299757600080fd5b919050565b600080604083850312156129af57600080fd5b6129b883612980565b946020939093013593505050565b600080600080600060a086880312156129de57600080fd5b6129e786612980565b9450602086013593506129fc60408701612980565b9250612a0a60608701612980565b949793965091946080013592915050565b6020808252825182820181905260009190848201906040850190845b81811015612a5c5783516001600160a01b031683529284019291840191600101612a37565b50909695505050505050565b60008083601f840112612a7a57600080fd5b50813567ffffffffffffffff811115612a9257600080fd5b6020830191508360208260051b8501011115612aad57600080fd5b9250929050565b60008060008060408587031215612aca57600080fd5b843567ffffffffffffffff80821115612ae257600080fd5b612aee88838901612a68565b90965094506020870135915080821115612b0757600080fd5b50612b1487828801612a68565b95989497509550505050565b60008060408385031215612b3357600080fd5b612b3c83612980565b9150612b4a60208401612980565b90509250929050565b600060208284031215612b6557600080fd5b5035919050565b600060208284031215612b7e57600080fd5b610b6d82612980565b803563ffffffff8116811461299757600080fd5b8035600581900b811461299757600080fd5b60ff811681146116ac57600080fd5b803561299781612bad565b6000806000838503610100811215612bde57600080fd5b84359350612bee60208601612980565b925060c0603f1982011215612c0257600080fd5b5060405160c0810181811067ffffffffffffffff82111715612c3457634e487b7160e01b600052604160045260246000fd5b8060405250612c4560408601612b87565b8152612c5360608601612b87565b6020820152612c6460808601612b9b565b6040820152612c7560a08601612b9b565b6060820152612c8660c08601612b9b565b6080820152612c9760e08601612bbc565b60a0820152809150509250925092565b60008060008060008060c08789031215612cc057600080fd5b612cc987612980565b955060208701359450612cde60408801612980565b9350612cec60608801612980565b92506080870135915060a087013590509295509295509295565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156107de576107de612d1c565b634e487b7160e01b600052603160045260246000fd5b6020808252601f908201527f45786368616e676550726f766964657220646f6573206e6f7420657869737400604082015260600190565b600060208284031215612da457600080fd5b81518015158114610b6d57600080fd5b600060208284031215612dc657600080fd5b5051919050565b9384526001600160a01b03928316602085015291166040830152606082015260800190565b600060018201612e0457612e04612d1c565b5060010190565b80820260008212600160ff1b84141615612e2757612e27612d1c565b81810583148215176107de576107de612d1c565b600060208284031215612e4d57600080fd5b8151610b6d81612bad565b600181815b80851115612e93578160001904821115612e7957612e79612d1c565b80851615612e8657918102915b93841c9390800290612e5d565b509250929050565b600082612eaa575060016107de565b81612eb7575060006107de565b8160018114612ecd5760028114612ed757612ef3565b60019150506107de565b60ff841115612ee857612ee8612d1c565b50506001821b6107de565b5060208310610133831016604e8410600b8410161715612f16575081810a6107de565b612f208383612e58565b8060001904821115612f3457612f34612d1c565b029392505050565b6000610b6d8383612e9b565b600082612f6557634e487b7160e01b600052601260045260246000fd5b600160ff1b821460001984141615612f7f57612f7f612d1c565b500590565b63ffffffff818116838216019080821115612fa157612fa1612d1c565b5092915050565b60008260050b8260050b028060050b9150808214612fa157612fa1612d1c565b60005b83811015612fe3578181015183820152602001612fcb565b50506000910152565b60008251612ffe818460208701612fc8565b9190910192915050565b808201828112600083128015821682158216171561302857613028612d1c565b505092915050565b602081526000825180602084015261304f816040850160208701612fc8565b601f01601f1916919091016040019291505056

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : test (bool): True

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001


Block Transaction Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0xa92Fb401f6aFBBF27338d4cE20322b102b35c51A
Loading...
Loading
Loading...
Loading
Loading...
Loading

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