ETH Price: $3,265.20 (+2.96%)
Gas: 9 Gwei

Contract

0xA36972E347E538E6C7Afb9f44FB10DDa7BBa9BA2
 
Transaction Hash
Method
Block
From
To
0x645465a1868ed6c29b51d912f8260e1fc4f5b32b28deee80d28ef7c2abb8ae85 Deposit Token By...(pending)2024-07-25 9:37:2628 hrs ago1721900246IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0xe5fbf07f1d038378704e831c1592bf65097b0faab3ef45d9df36ac0190c7f3b6 Deposit Ether(pending)2024-07-25 7:31:5830 hrs ago1721892718IN
0xA36972E3...a7BBa9BA2
0.00158642 ETH(Pending)(Pending)
0x8d3c606c8b14629ce4bbfff6994b8efc024f24e31c0c8caf393e39950e7f8151 Deposit Token By...(pending)2024-07-25 7:29:0430 hrs ago1721892544IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0xad70415b78bb4f4d4e80c20a2dfa50e76c0862cd893e89f140214d92344c0b90 Deposit Ether(pending)2024-07-25 7:27:4430 hrs ago1721892464IN
0xA36972E3...a7BBa9BA2
0.17133725 ETH(Pending)(Pending)
0x1e864e8faeac1919ed95c039877bef8a97d271438a2963b93b04d56c6c94fe08 Deposit Token By...(pending)2024-07-25 7:25:1930 hrs ago1721892319IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0x0c6d5ddfe97bd47f96888681615c3e7eb316a34c7f7ba70b402a5f263d990692 Deposit Ether(pending)2024-07-25 7:23:3430 hrs ago1721892214IN
0xA36972E3...a7BBa9BA2
0.0405 ETH(Pending)(Pending)
0x48791ec72148055e9442797a125b37fa6e7b3942176b3b506b5c68467d5c9335 Deposit Ether(pending)2024-07-25 7:18:5330 hrs ago1721891933IN
0xA36972E3...a7BBa9BA2
0.002 ETH(Pending)(Pending)
0x11f9ca62cca3042322a826e7511ba534c27768c4a6f756039f662dba1ec385e1 Deposit Ether(pending)2024-07-25 7:15:4631 hrs ago1721891746IN
0xA36972E3...a7BBa9BA2
0.003 ETH(Pending)(Pending)
0xbfb2ad043a1be00c41fce99462c1f85d331a2fa9d66e6fa1c41b1a39f9534252 Deposit Token By...(pending)2024-07-25 7:13:1931 hrs ago1721891599IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0x5b7427c4be08599e1bd264e259caebd0fe3b380c13cfa00684aa3d4e303bb99b Deposit Ether(pending)2024-07-25 7:12:3431 hrs ago1721891554IN
0xA36972E3...a7BBa9BA2
0.00920722 ETH(Pending)(Pending)
0xf38d5195d623229c5484dfb99ea3d4ef3c724af1d5bdadfa656ed7c4de23ccdc Deposit Ether(pending)2024-07-25 7:12:1631 hrs ago1721891536IN
0xA36972E3...a7BBa9BA2
0.09191895 ETH(Pending)(Pending)
0x66ec25a18daac79d51dc107ad8c97415ff3b6ba8e51ca681a72411f5585b1afb Deposit Ether(pending)2024-07-25 7:11:5931 hrs ago1721891519IN
0xA36972E3...a7BBa9BA2
0.00418158 ETH(Pending)(Pending)
0x447dbb02883f7d679b587faa2c22471ad5b9df25645308eb6e08c3113b1893f4 Deposit Ether(pending)2024-07-25 7:09:2831 hrs ago1721891368IN
0xA36972E3...a7BBa9BA2
0.005 ETH(Pending)(Pending)
0x1898c3bf491ab3d509364d84bba279150b6cdf5c824923c49c1dc19ff9fa5a00 Deposit Ether(pending)2024-07-25 7:09:0631 hrs ago1721891346IN
0xA36972E3...a7BBa9BA2
0.00074097 ETH(Pending)(Pending)
0x7707416668b46dccbe40fc70bd26ffab2f920323d684cb55f62d4e75019cdc01 Deposit Ether(pending)2024-07-25 7:08:2831 hrs ago1721891308IN
0xA36972E3...a7BBa9BA2
0.04991895 ETH(Pending)(Pending)
0x7772ebed6444216a77c530ffc6e8a529212307d00a7c5744563b7d93770a739a Deposit Ether(pending)2024-07-25 7:08:1431 hrs ago1721891294IN
0xA36972E3...a7BBa9BA2
0.00091895 ETH(Pending)(Pending)
0xe52b182803d7492ab7db70b98f18906989a228a2e4493bc857fb9f7923bd0da9 Deposit Ether(pending)2024-07-25 7:06:5531 hrs ago1721891215IN
0xA36972E3...a7BBa9BA2
0.01677293 ETH(Pending)(Pending)
0x738076293c91df0ea27866271f9d9fa33931a6c92804ab6512288b00eeb54bac Deposit Ether(pending)2024-07-25 7:06:5431 hrs ago1721891214IN
0xA36972E3...a7BBa9BA2
0.00020259 ETH(Pending)(Pending)
0xd90a5ad759791a40ff3f443e6f514ed202ad305d6c566fa2c8f5fdb1a1219cba Deposit Ether(pending)2024-07-25 7:05:5231 hrs ago1721891152IN
0xA36972E3...a7BBa9BA2
0.0009163 ETH(Pending)(Pending)
0x25d3e3da4c7e4b707ab5823086942fde4a717601e6b2413ce06fac57e62c9827 Deposit Token By...(pending)2024-07-22 9:19:074 days ago1721639947IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0xbfd983537919e3014e7daf26259620eff84b43bd5c7b0487944f0eab4f8d0c26 Deposit Ether(pending)2024-07-21 3:46:065 days ago1721533566IN
0xA36972E3...a7BBa9BA2
0.00086717 ETH(Pending)(Pending)
0x63a466e1804f117f4347649952d2dff9c8023370f889b38fd62a2e68c620db1e Exit Wallet(pending)2024-07-21 3:38:515 days ago1721533131IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0x40a542d596a4bee2bff65355d9cda47073c92c1f7f8b464bd2b011762750e4e1 Deposit Token By...(pending)2024-07-21 2:13:275 days ago1721528007IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
0x36ab92b6efb6f1a66b37de5e9e86a4109376a5f3e07d87298746f31a491f478a Deposit Ether(pending)2024-07-21 2:01:035 days ago1721527263IN
0xA36972E3...a7BBa9BA2
0.00551061 ETH(Pending)(Pending)
0x8d36a87065d39cda5a340b14acf0c0a61861f69ca704369e4ad418877b70c5d1 Deposit Token By...(pending)2024-07-21 1:56:165 days ago1721526976IN
0xA36972E3...a7BBa9BA2
0 ETH(Pending)(Pending)
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
155590662022-09-18 7:52:47677 days ago1663487567
0xA36972E3...a7BBa9BA2
0.00001229 ETH
153827082022-08-21 7:41:59705 days ago1661067719
0xA36972E3...a7BBa9BA2
0.038 ETH
152466382022-07-30 23:00:50726 days ago1659222050
0xA36972E3...a7BBa9BA2
0.00300612 ETH
152466092022-07-30 22:54:41726 days ago1659221681
0xA36972E3...a7BBa9BA2
0.00771718 ETH
143331792022-03-06 11:55:58873 days ago1646567758
0xA36972E3...a7BBa9BA2
0.01 ETH
138352632021-12-19 11:21:30950 days ago1639912890
0xA36972E3...a7BBa9BA2
0.02 ETH
137764952021-12-10 9:13:26959 days ago1639127606
0xA36972E3...a7BBa9BA2
0.25 ETH
137583692021-12-07 11:49:06962 days ago1638877746
0xA36972E3...a7BBa9BA2
0.015 ETH
137070922021-11-29 6:41:28970 days ago1638168088
0xA36972E3...a7BBa9BA2
0.29 ETH
137069312021-11-29 6:04:39970 days ago1638165879
0xA36972E3...a7BBa9BA2
0.13 ETH
137066602021-11-29 4:59:18970 days ago1638161958
0xA36972E3...a7BBa9BA2
0.67 ETH
137025332021-11-28 13:00:16971 days ago1638104416
0xA36972E3...a7BBa9BA2
0.2 ETH
136995772021-11-28 1:51:10971 days ago1638064270
0xA36972E3...a7BBa9BA2
0.03 ETH
136949692021-11-27 8:15:51972 days ago1638000951
0xA36972E3...a7BBa9BA2
0.15619741 ETH
136757462021-11-24 6:50:41975 days ago1637736641
0xA36972E3...a7BBa9BA2
0.01818531 ETH
136731332021-11-23 21:13:10975 days ago1637701990
0xA36972E3...a7BBa9BA2
0.24 ETH
136726302021-11-23 19:16:02975 days ago1637694962
0xA36972E3...a7BBa9BA2
0.641 ETH
136455262021-11-19 12:18:48980 days ago1637324328
0xA36972E3...a7BBa9BA2
0.03 ETH
136443622021-11-19 7:35:55980 days ago1637307355
0xA36972E3...a7BBa9BA2
0.9 ETH
136432782021-11-19 3:21:01980 days ago1637292061
0xA36972E3...a7BBa9BA2
0.2 ETH
136397692021-11-18 14:07:48981 days ago1637244468
0xA36972E3...a7BBa9BA2
0.4 ETH
136396802021-11-18 13:47:06981 days ago1637243226
0xA36972E3...a7BBa9BA2
0.03 ETH
136292482021-11-16 22:31:48982 days ago1637101908
0xA36972E3...a7BBa9BA2
0.15568504 ETH
136283752021-11-16 19:15:10982 days ago1637090110
0xA36972E3...a7BBa9BA2
0.02 ETH
136282852021-11-16 18:53:18982 days ago1637088798
0xA36972E3...a7BBa9BA2
0.04 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Exchange

Compiler Version
v0.6.8+commit.0bbfe453

Optimization Enabled:
Yes with 49 runs

Other Settings:
default evmVersion, GNU LGPLv3 license

Contract Source Code (Solidity Multiple files format)

File 1 of 12: Exchange.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

import { Address } from './Address.sol';
import { ECDSA } from './ECDSA.sol';
import {
  SafeMath as SafeMath256
} from './SafeMath.sol';

import { AssetRegistry } from './AssetRegistry.sol';
import { AssetTransfers } from './AssetTransfers.sol';
import { AssetUnitConversions } from './AssetUnitConversions.sol';
import { Owned } from './Owned.sol';
import { SafeMath64 } from './SafeMath64.sol';
import { Signatures } from './Signatures.sol';
import {
  Enums,
  ICustodian,
  IERC20,
  IExchange,
  Structs
} from './Interfaces.sol';
import { UUID } from './UUID.sol';


/**
 * @notice The Exchange contract. Implements all deposit, trade, and withdrawal logic and associated balance tracking
 *
 * @dev The term `asset` refers collectively to ETH and ERC-20 tokens, the term `token` refers only to the latter
 * @dev Events with indexed string parameters (Deposited and TradeExecuted) only log the hash values for those
 * parameters, from which the original raw string values cannot be retrieved. For convenience these events contain
 * the un-indexed string parameter values in addition to the indexed values
 */
contract Exchange is IExchange, Owned {
  using SafeMath64 for uint64;
  using SafeMath256 for uint256;
  using AssetRegistry for AssetRegistry.Storage;

  // Events //

  /**
   * @notice Emitted when an admin changes the Chain Propagation Period tunable parameter with `setChainPropagationPeriod`
   */
  event ChainPropagationPeriodChanged(uint256 previousValue, uint256 newValue);
  /**
   * @notice Emitted when a user deposits ETH with `depositEther` or a token with `depositAsset` or `depositAssetBySymbol`
   */
  event Deposited(
    uint64 index,
    address indexed wallet,
    address indexed assetAddress,
    string indexed assetSymbolIndex,
    string assetSymbol,
    uint64 quantityInPips,
    uint64 newExchangeBalanceInPips,
    uint256 newExchangeBalanceInAssetUnits
  );
  /**
   * @notice Emitted when an admin changes the Dispatch Wallet tunable parameter with `setDispatcher`
   */
  event DispatcherChanged(address previousValue, address newValue);

  /**
   * @notice Emitted when an admin changes the Fee Wallet tunable parameter with `setFeeWallet`
   */
  event FeeWalletChanged(address previousValue, address newValue);

  /**
   * @notice Emitted when a user invalidates an order nonce with `invalidateOrderNonce`
   */
  event OrderNonceInvalidated(
    address indexed wallet,
    uint128 nonce,
    uint128 timestampInMs,
    uint256 effectiveBlockNumber
  );
  /**
   * @notice Emitted when an admin initiates the token registration process with `registerToken`
   */
  event TokenRegistered(
    IERC20 indexed assetAddress,
    string assetSymbol,
    uint8 decimals
  );
  /**
   * @notice Emitted when an admin finalizes the token registration process with `confirmAssetRegistration`, after
   * which it can be deposited, traded, or withdrawn
   */
  event TokenRegistrationConfirmed(
    IERC20 indexed assetAddress,
    string assetSymbol,
    uint8 decimals
  );
  /**
   * @notice Emitted when an admin adds a symbol to a previously registered and confirmed token
   * via `addTokenSymbol`
   */
  event TokenSymbolAdded(IERC20 indexed assetAddress, string assetSymbol);
  /**
   * @notice Emitted when the Dispatcher Wallet submits a trade for execution with `executeTrade`
   */
  event TradeExecuted(
    address buyWallet,
    address sellWallet,
    string indexed baseAssetSymbolIndex,
    string indexed quoteAssetSymbolIndex,
    string baseAssetSymbol,
    string quoteAssetSymbol,
    uint64 baseQuantityInPips,
    uint64 quoteQuantityInPips,
    uint64 tradePriceInPips,
    bytes32 buyOrderHash,
    bytes32 sellOrderHash
  );
  /**
   * @notice Emitted when a user invokes the Exit Wallet mechanism with `exitWallet`
   */
  event WalletExited(address indexed wallet, uint256 effectiveBlockNumber);
  /**
   * @notice Emitted when a user withdraws an asset balance through the Exit Wallet mechanism with `withdrawExit`
   */
  event WalletExitWithdrawn(
    address indexed wallet,
    address indexed assetAddress,
    string assetSymbol,
    uint64 quantityInPips,
    uint64 newExchangeBalanceInPips,
    uint256 newExchangeBalanceInAssetUnits
  );
  /**
   * @notice Emitted when a user clears the exited status of a wallet previously exited with `exitWallet`
   */
  event WalletExitCleared(address indexed wallet);
  /**
   * @notice Emitted when the Dispatcher Wallet submits a withdrawal with `withdraw`
   */
  event Withdrawn(
    address indexed wallet,
    address indexed assetAddress,
    string assetSymbol,
    uint64 quantityInPips,
    uint64 newExchangeBalanceInPips,
    uint256 newExchangeBalanceInAssetUnits
  );

  // Internally used structs //

  struct NonceInvalidation {
    bool exists;
    uint64 timestampInMs;
    uint256 effectiveBlockNumber;
  }
  struct WalletExit {
    bool exists;
    uint256 effectiveBlockNumber;
  }

  // Storage //

  // Asset registry data
  AssetRegistry.Storage _assetRegistry;
  // Mapping of order wallet hash => isComplete
  mapping(bytes32 => bool) _completedOrderHashes;
  // Mapping of withdrawal wallet hash => isComplete
  mapping(bytes32 => bool) _completedWithdrawalHashes;
  address payable _custodian;
  uint64 _depositIndex;
  // Mapping of wallet => asset => balance
  mapping(address => mapping(address => uint64)) _balancesInPips;
  // Mapping of wallet => last invalidated timestampInMs
  mapping(address => NonceInvalidation) _nonceInvalidations;
  // Mapping of order hash => filled quantity in pips
  mapping(bytes32 => uint64) _partiallyFilledOrderQuantitiesInPips;
  mapping(address => WalletExit) _walletExits;
  // Tunable parameters
  uint256 _chainPropagationPeriod;
  address _dispatcherWallet;
  address _feeWallet;

  // Constant values //

  uint256 constant _maxChainPropagationPeriod = (7 * 24 * 60 * 60) / 15; // 1 week at 15s/block
  uint64 constant _maxTradeFeeBasisPoints = 20 * 100; // 20%;
  uint64 constant _maxWithdrawalFeeBasisPoints = 20 * 100; // 20%;

  /**
   * @notice Instantiate a new `Exchange` contract
   *
   * @dev Sets `_owner` and `_admin` to `msg.sender` */
  constructor() public Owned() {}

  /**
   * @notice Sets the address of the `Custodian` contract
   *
   * @dev The `Custodian` accepts `Exchange` and `Governance` addresses in its constructor, after
   * which they can only be changed by the `Governance` contract itself. Therefore the `Custodian`
   * must be deployed last and its address set here on an existing `Exchange` contract. This value
   * is immutable once set and cannot be changed again
   *
   * @param newCustodian The address of the `Custodian` contract deployed against this `Exchange`
   * contract's address
   */
  function setCustodian(address payable newCustodian) external onlyAdmin {
    require(_custodian == address(0x0), 'Custodian can only be set once');
    require(Address.isContract(newCustodian), 'Invalid address');

    _custodian = newCustodian;
  }

  /*** Tunable parameters ***/

  /**
   * @notice Sets a new Chain Propagation Period - the block delay after which order nonce invalidations
   * are respected by `executeTrade` and wallet exits are respected by `executeTrade` and `withdraw`
   *
   * @param newChainPropagationPeriod The new Chain Propagation Period expressed as a number of blocks. Must
   * be less than `_maxChainPropagationPeriod`
   */
  function setChainPropagationPeriod(uint256 newChainPropagationPeriod)
    external
    onlyAdmin
  {
    require(
      newChainPropagationPeriod < _maxChainPropagationPeriod,
      'Must be less than 1 week'
    );

    uint256 oldChainPropagationPeriod = _chainPropagationPeriod;
    _chainPropagationPeriod = newChainPropagationPeriod;

    emit ChainPropagationPeriodChanged(
      oldChainPropagationPeriod,
      newChainPropagationPeriod
    );
  }

  /**
   * @notice Sets the address of the Fee wallet
   *
   * @dev Trade and Withdraw fees will accrue in the `_balancesInPips` mappings for this wallet
   *
   * @param newFeeWallet The new Fee wallet. Must be different from the current one
   */
  function setFeeWallet(address newFeeWallet) external onlyAdmin {
    require(newFeeWallet != address(0x0), 'Invalid wallet address');
    require(
      newFeeWallet != _feeWallet,
      'Must be different from current fee wallet'
    );

    address oldFeeWallet = _feeWallet;
    _feeWallet = newFeeWallet;

    emit FeeWalletChanged(oldFeeWallet, newFeeWallet);
  }

  // Accessors //

  /**
   * @notice Load a wallet's balance by asset address, in asset units
   *
   * @param wallet The wallet address to load the balance for. Can be different from `msg.sender`
   * @param assetAddress The asset address to load the wallet's balance for
   *
   * @return The quantity denominated in asset units of asset at `assetAddress` currently
   * deposited by `wallet`
   */
  function loadBalanceInAssetUnitsByAddress(
    address wallet,
    address assetAddress
  ) external view returns (uint256) {
    require(wallet != address(0x0), 'Invalid wallet address');

    Structs.Asset memory asset = _assetRegistry.loadAssetByAddress(
      assetAddress
    );
    return
      AssetUnitConversions.pipsToAssetUnits(
        _balancesInPips[wallet][assetAddress],
        asset.decimals
      );
  }

  /**
   * @notice Load a wallet's balance by asset address, in asset units
   *
   * @param wallet The wallet address to load the balance for. Can be different from `msg.sender`
   * @param assetSymbol The asset symbol to load the wallet's balance for
   *
   * @return The quantity denominated in asset units of asset `assetSymbol` currently deposited
   * by `wallet`
   */
  function loadBalanceInAssetUnitsBySymbol(
    address wallet,
    string calldata assetSymbol
  ) external view returns (uint256) {
    require(wallet != address(0x0), 'Invalid wallet address');

    Structs.Asset memory asset = _assetRegistry.loadAssetBySymbol(
      assetSymbol,
      getCurrentTimestampInMs()
    );
    return
      AssetUnitConversions.pipsToAssetUnits(
        _balancesInPips[wallet][asset.assetAddress],
        asset.decimals
      );
  }

  /**
   * @notice Load a wallet's balance by asset address, in pips
   *
   * @param wallet The wallet address to load the balance for. Can be different from `msg.sender`
   * @param assetAddress The asset address to load the wallet's balance for
   *
   * @return The quantity denominated in pips of asset at `assetAddress` currently deposited by `wallet`
   */
  function loadBalanceInPipsByAddress(address wallet, address assetAddress)
    external
    view
    returns (uint64)
  {
    require(wallet != address(0x0), 'Invalid wallet address');

    return _balancesInPips[wallet][assetAddress];
  }

  /**
   * @notice Load a wallet's balance by asset symbol, in pips
   *
   * @param wallet The wallet address to load the balance for. Can be different from `msg.sender`
   * @param assetSymbol The asset symbol to load the wallet's balance for
   *
   * @return The quantity denominated in pips of asset with `assetSymbol` currently deposited by `wallet`
   */
  function loadBalanceInPipsBySymbol(
    address wallet,
    string calldata assetSymbol
  ) external view returns (uint64) {
    require(wallet != address(0x0), 'Invalid wallet address');

    address assetAddress = _assetRegistry
      .loadAssetBySymbol(assetSymbol, getCurrentTimestampInMs())
      .assetAddress;
    return _balancesInPips[wallet][assetAddress];
  }

  /**
   * @notice Load the address of the Fee wallet
   *
   * @return The address of the Fee wallet
   */
  function loadFeeWallet() external view returns (address) {
    return _feeWallet;
  }

  /**
   * @notice Load the quantity filled so far for a partially filled orders

   * @dev Invalidating an order nonce will not clear partial fill quantities for earlier orders because
   * the gas cost would potentially be unbound
   *
   * @param orderHash The order hash as originally signed by placing wallet that uniquely identifies an order
   *
   * @return For partially filled orders, the amount filled so far in pips. For orders in all other states, 0
   */
  function loadPartiallyFilledOrderQuantityInPips(bytes32 orderHash)
    external
    view
    returns (uint64)
  {
    return _partiallyFilledOrderQuantitiesInPips[orderHash];
  }

  // Depositing //

  /**
   * @notice Deposit ETH
   */
  function depositEther() external payable {
    deposit(msg.sender, address(0x0), msg.value);
  }

  /**
   * @notice Deposit `IERC20` compliant tokens
   *
   * @param tokenAddress The token contract address
   * @param quantityInAssetUnits The quantity to deposit. The sending wallet must first call the `approve` method on
   * the token contract for at least this quantity first
   */
  function depositTokenByAddress(
    IERC20 tokenAddress,
    uint256 quantityInAssetUnits
  ) external {
    require(
      address(tokenAddress) != address(0x0),
      'Use depositEther to deposit Ether'
    );
    deposit(msg.sender, address(tokenAddress), quantityInAssetUnits);
  }

  /**
   * @notice Deposit `IERC20` compliant tokens
   *
   * @param assetSymbol The case-sensitive symbol string for the token
   * @param quantityInAssetUnits The quantity to deposit. The sending wallet must first call the `approve` method on
   * the token contract for at least this quantity first
   */
  function depositTokenBySymbol(
    string calldata assetSymbol,
    uint256 quantityInAssetUnits
  ) external {
    IERC20 tokenAddress = IERC20(
      _assetRegistry
        .loadAssetBySymbol(assetSymbol, getCurrentTimestampInMs())
        .assetAddress
    );
    require(
      address(tokenAddress) != address(0x0),
      'Use depositEther to deposit ETH'
    );

    deposit(msg.sender, address(tokenAddress), quantityInAssetUnits);
  }

  function deposit(
    address payable wallet,
    address assetAddress,
    uint256 quantityInAssetUnits
  ) private {
    // Calling exitWallet disables deposits immediately on mining, in contrast to withdrawals and
    // trades which respect the Chain Propagation Period given by `effectiveBlockNumber` via
    // `isWalletExitFinalized`
    require(!_walletExits[wallet].exists, 'Wallet exited');

    Structs.Asset memory asset = _assetRegistry.loadAssetByAddress(
      assetAddress
    );
    uint64 quantityInPips = AssetUnitConversions.assetUnitsToPips(
      quantityInAssetUnits,
      asset.decimals
    );
    require(quantityInPips > 0, 'Quantity is too low');

    // Convert from pips back into asset units to remove any fractional amount that is too small
    // to express in pips. If the asset is ETH, this leftover fractional amount accumulates as dust
    // in the `Exchange` contract. If the asset is a token the `Exchange` will call `transferFrom`
    // without this fractional amount and there will be no dust
    uint256 quantityInAssetUnitsWithoutFractionalPips = AssetUnitConversions
      .pipsToAssetUnits(quantityInPips, asset.decimals);

    // If the asset is ETH then the funds were already assigned to this contract via msg.value. If
    // the asset is a token, additionally call the transferFrom function on the token contract for
    // the pre-approved asset quantity
    if (assetAddress != address(0x0)) {
      AssetTransfers.transferFrom(
        wallet,
        IERC20(assetAddress),
        quantityInAssetUnitsWithoutFractionalPips
      );
    }
    // Forward the funds to the `Custodian`
    AssetTransfers.transferTo(
      _custodian,
      assetAddress,
      quantityInAssetUnitsWithoutFractionalPips
    );

    uint64 newExchangeBalanceInPips = _balancesInPips[wallet][assetAddress].add(
      quantityInPips
    );
    uint256 newExchangeBalanceInAssetUnits = AssetUnitConversions
      .pipsToAssetUnits(newExchangeBalanceInPips, asset.decimals);

    // Update balance with actual transferred quantity
    _balancesInPips[wallet][assetAddress] = newExchangeBalanceInPips;
    _depositIndex++;

    emit Deposited(
      _depositIndex,
      wallet,
      assetAddress,
      asset.symbol,
      asset.symbol,
      quantityInPips,
      newExchangeBalanceInPips,
      newExchangeBalanceInAssetUnits
    );
  }

  // Invalidation //

  /**
   * @notice Invalidate all order nonces with a timestampInMs lower than the one provided
   *
   * @param nonce A Version 1 UUID. After calling and once the Chain Propagation Period has elapsed,
   * `executeTrade` will reject order nonces from this wallet with a timestampInMs component lower than
   * the one provided
   */
  function invalidateOrderNonce(uint128 nonce) external {
    uint64 timestampInMs = UUID.getTimestampInMsFromUuidV1(nonce);
    // Enforce a maximum skew for invalidating nonce timestamps in the future so the user doesn't
    // lock their wallet from trades indefinitely
    require(
      timestampInMs < getOneDayFromNowInMs(),
      'Nonce timestamp too far in future'
    );

    if (_nonceInvalidations[msg.sender].exists) {
      require(
        _nonceInvalidations[msg.sender].timestampInMs < timestampInMs,
        'Nonce timestamp already invalidated'
      );
      require(
        _nonceInvalidations[msg.sender].effectiveBlockNumber <= block.number,
        'Previous invalidation awaiting chain propagation'
      );
    }

    // Changing the Chain Propagation Period will not affect the effectiveBlockNumber for this invalidation
    uint256 effectiveBlockNumber = block.number + _chainPropagationPeriod;
    _nonceInvalidations[msg.sender] = NonceInvalidation(
      true,
      timestampInMs,
      effectiveBlockNumber
    );

    emit OrderNonceInvalidated(
      msg.sender,
      nonce,
      timestampInMs,
      effectiveBlockNumber
    );
  }

  // Withdrawing //

  /**
   * @notice Settles a user withdrawal submitted off-chain. Calls restricted to currently whitelisted Dispatcher wallet
   *
   * @param withdrawal A `Structs.Withdrawal` struct encoding the parameters of the withdrawal
   */
  function withdraw(Structs.Withdrawal memory withdrawal)
    public
    override
    onlyDispatcher
  {
    // Validations
    require(!isWalletExitFinalized(withdrawal.walletAddress), 'Wallet exited');
    require(
      getFeeBasisPoints(withdrawal.gasFeeInPips, withdrawal.quantityInPips) <=
        _maxWithdrawalFeeBasisPoints,
      'Excessive withdrawal fee'
    );
    bytes32 withdrawalHash = validateWithdrawalSignature(withdrawal);
    require(
      !_completedWithdrawalHashes[withdrawalHash],
      'Hash already withdrawn'
    );

    // If withdrawal is by asset symbol (most common) then resolve to asset address
    Structs.Asset memory asset = withdrawal.withdrawalType ==
      Enums.WithdrawalType.BySymbol
      ? _assetRegistry.loadAssetBySymbol(
        withdrawal.assetSymbol,
        UUID.getTimestampInMsFromUuidV1(withdrawal.nonce)
      )
      : _assetRegistry.loadAssetByAddress(withdrawal.assetAddress);

    // SafeMath reverts if balance is overdrawn
    uint64 netAssetQuantityInPips = withdrawal.quantityInPips.sub(
      withdrawal.gasFeeInPips
    );
    uint256 netAssetQuantityInAssetUnits = AssetUnitConversions
      .pipsToAssetUnits(netAssetQuantityInPips, asset.decimals);
    uint64 newExchangeBalanceInPips = _balancesInPips[withdrawal
      .walletAddress][asset.assetAddress]
      .sub(withdrawal.quantityInPips);
    uint256 newExchangeBalanceInAssetUnits = AssetUnitConversions
      .pipsToAssetUnits(newExchangeBalanceInPips, asset.decimals);

    _balancesInPips[withdrawal.walletAddress][asset
      .assetAddress] = newExchangeBalanceInPips;
    _balancesInPips[_feeWallet][asset
      .assetAddress] = _balancesInPips[_feeWallet][asset.assetAddress].add(
      withdrawal.gasFeeInPips
    );

    ICustodian(_custodian).withdraw(
      withdrawal.walletAddress,
      asset.assetAddress,
      netAssetQuantityInAssetUnits
    );

    _completedWithdrawalHashes[withdrawalHash] = true;

    emit Withdrawn(
      withdrawal.walletAddress,
      asset.assetAddress,
      asset.symbol,
      withdrawal.quantityInPips,
      newExchangeBalanceInPips,
      newExchangeBalanceInAssetUnits
    );
  }

  // Wallet exits //

  /**
   * @notice Flags the sending wallet as exited, immediately disabling deposits upon mining.
   * After the Chain Propagation Period passes trades and withdrawals are also disabled for the wallet,
   * and assets may then be withdrawn one at a time via `withdrawExit`
   */
  function exitWallet() external {
    require(!_walletExits[msg.sender].exists, 'Wallet already exited');

    _walletExits[msg.sender] = WalletExit(
      true,
      block.number + _chainPropagationPeriod
    );

    emit WalletExited(msg.sender, block.number + _chainPropagationPeriod);
  }

  /**
   * @notice Withdraw the entire balance of an asset for an exited wallet. The Chain Propagation Period must
   * have already passed since calling `exitWallet` on `assetAddress`
   *
   * @param assetAddress The address of the asset to withdraw
   */
  function withdrawExit(address assetAddress) external {
    require(isWalletExitFinalized(msg.sender), 'Wallet exit not finalized');

    Structs.Asset memory asset = _assetRegistry.loadAssetByAddress(
      assetAddress
    );
    uint64 balanceInPips = _balancesInPips[msg.sender][assetAddress];
    uint256 balanceInAssetUnits = AssetUnitConversions.pipsToAssetUnits(
      balanceInPips,
      asset.decimals
    );

    require(balanceInAssetUnits > 0, 'No balance for asset');
    _balancesInPips[msg.sender][assetAddress] = 0;
    ICustodian(_custodian).withdraw(
      msg.sender,
      assetAddress,
      balanceInAssetUnits
    );

    emit WalletExitWithdrawn(
      msg.sender,
      assetAddress,
      asset.symbol,
      balanceInPips,
      0,
      0
    );
  }

  /**
   * @notice Clears exited status of sending wallet. Upon mining immediately enables
   * deposits, trades, and withdrawals by sending wallet
   */
  function clearWalletExit() external {
    require(_walletExits[msg.sender].exists, 'Wallet not exited');

    delete _walletExits[msg.sender];

    emit WalletExitCleared(msg.sender);
  }

  function isWalletExitFinalized(address wallet) internal view returns (bool) {
    WalletExit storage exit = _walletExits[wallet];
    return exit.exists && exit.effectiveBlockNumber <= block.number;
  }

  // Trades //

  /**
   * @notice Settles a trade between two orders submitted and matched off-chain
   *
   * @dev As a gas optimization, base and quote symbols are passed in separately and combined to verify
   * the wallet hash, since this is cheaper than splitting the market symbol into its two constituent asset symbols
   * @dev Stack level too deep if declared external
   *
   * @param buy A `Structs.Order` struct encoding the parameters of the buy-side order (receiving base, giving quote)
   * @param sell A `Structs.Order` struct encoding the parameters of the sell-side order (giving base, receiving quote)
   * @param trade A `Structs.Trade` struct encoding the parameters of this trade execution of the counterparty orders
   */
  function executeTrade(
    Structs.Order memory buy,
    Structs.Order memory sell,
    Structs.Trade memory trade
  ) public override onlyDispatcher {
    require(
      !isWalletExitFinalized(buy.walletAddress),
      'Buy wallet exit finalized'
    );
    require(
      !isWalletExitFinalized(sell.walletAddress),
      'Sell wallet exit finalized'
    );
    require(
      buy.walletAddress != sell.walletAddress,
      'Self-trading not allowed'
    );

    validateAssetPair(buy, sell, trade);
    validateLimitPrices(buy, sell, trade);
    validateOrderNonces(buy, sell);
    (bytes32 buyHash, bytes32 sellHash) = validateOrderSignatures(
      buy,
      sell,
      trade
    );
    validateTradeFees(trade);

    updateOrderFilledQuantities(buy, buyHash, sell, sellHash, trade);
    updateBalancesForTrade(buy, sell, trade);

    emit TradeExecuted(
      buy.walletAddress,
      sell.walletAddress,
      trade.baseAssetSymbol,
      trade.quoteAssetSymbol,
      trade.baseAssetSymbol,
      trade.quoteAssetSymbol,
      trade.grossBaseQuantityInPips,
      trade.grossQuoteQuantityInPips,
      trade.priceInPips,
      buyHash,
      sellHash
    );
  }

  // Updates buyer, seller, and fee wallet balances for both assets in trade pair according to trade parameters
  function updateBalancesForTrade(
    Structs.Order memory buy,
    Structs.Order memory sell,
    Structs.Trade memory trade
  ) private {
    // Seller gives base asset including fees
    _balancesInPips[sell.walletAddress][trade
      .baseAssetAddress] = _balancesInPips[sell.walletAddress][trade
      .baseAssetAddress]
      .sub(trade.grossBaseQuantityInPips);
    // Buyer receives base asset minus fees
    _balancesInPips[buy.walletAddress][trade
      .baseAssetAddress] = _balancesInPips[buy.walletAddress][trade
      .baseAssetAddress]
      .add(trade.netBaseQuantityInPips);

    // Buyer gives quote asset including fees
    _balancesInPips[buy.walletAddress][trade
      .quoteAssetAddress] = _balancesInPips[buy.walletAddress][trade
      .quoteAssetAddress]
      .sub(trade.grossQuoteQuantityInPips);
    // Seller receives quote asset minus fees
    _balancesInPips[sell.walletAddress][trade
      .quoteAssetAddress] = _balancesInPips[sell.walletAddress][trade
      .quoteAssetAddress]
      .add(trade.netQuoteQuantityInPips);

    // Maker and taker fees to fee wallet
    _balancesInPips[_feeWallet][trade
      .makerFeeAssetAddress] = _balancesInPips[_feeWallet][trade
      .makerFeeAssetAddress]
      .add(trade.makerFeeQuantityInPips);
    _balancesInPips[_feeWallet][trade
      .takerFeeAssetAddress] = _balancesInPips[_feeWallet][trade
      .takerFeeAssetAddress]
      .add(trade.takerFeeQuantityInPips);
  }

  function updateOrderFilledQuantities(
    Structs.Order memory buyOrder,
    bytes32 buyOrderHash,
    Structs.Order memory sellOrder,
    bytes32 sellOrderHash,
    Structs.Trade memory trade
  ) private {
    updateOrderFilledQuantity(buyOrder, buyOrderHash, trade);
    updateOrderFilledQuantity(sellOrder, sellOrderHash, trade);
  }

  // Update filled quantities tracking for order to prevent over- or double-filling orders
  function updateOrderFilledQuantity(
    Structs.Order memory order,
    bytes32 orderHash,
    Structs.Trade memory trade
  ) private {
    require(!_completedOrderHashes[orderHash], 'Order double filled');

    // Total quantity of above filled as a result of all trade executions, including this one
    uint64 newFilledQuantityInPips;

    // Market orders can express quantity in quote terms, and can be partially filled by multiple
    // limit maker orders necessitating tracking partially filled amounts in quote terms to
    // determine completion
    if (order.isQuantityInQuote) {
      require(
        isMarketOrderType(order.orderType),
        'Order quote quantity only valid for market orders'
      );
      newFilledQuantityInPips = trade.grossQuoteQuantityInPips.add(
        _partiallyFilledOrderQuantitiesInPips[orderHash]
      );
    } else {
      // All other orders track partially filled quantities in base terms
      newFilledQuantityInPips = trade.grossBaseQuantityInPips.add(
        _partiallyFilledOrderQuantitiesInPips[orderHash]
      );
    }

    require(
      newFilledQuantityInPips <= order.quantityInPips,
      'Order overfilled'
    );

    if (newFilledQuantityInPips < order.quantityInPips) {
      // If the order was partially filled, track the new filled quantity
      _partiallyFilledOrderQuantitiesInPips[orderHash] = newFilledQuantityInPips;
    } else {
      // If the order was completed, delete any partial fill tracking and instead track its completion
      // to prevent future double fills
      delete _partiallyFilledOrderQuantitiesInPips[orderHash];
      _completedOrderHashes[orderHash] = true;
    }
  }

  // Validations //

  function validateAssetPair(
    Structs.Order memory buy,
    Structs.Order memory sell,
    Structs.Trade memory trade
  ) private view {
    require(
      trade.baseAssetAddress != trade.quoteAssetAddress,
      'Base and quote assets must be different'
    );

    // Buy order market pair
    Structs.Asset memory buyBaseAsset = _assetRegistry.loadAssetBySymbol(
      trade.baseAssetSymbol,
      UUID.getTimestampInMsFromUuidV1(buy.nonce)
    );
    Structs.Asset memory buyQuoteAsset = _assetRegistry.loadAssetBySymbol(
      trade.quoteAssetSymbol,
      UUID.getTimestampInMsFromUuidV1(buy.nonce)
    );
    require(
      buyBaseAsset.assetAddress == trade.baseAssetAddress &&
        buyQuoteAsset.assetAddress == trade.quoteAssetAddress,
      'Buy order market symbol address resolution mismatch'
    );

    // Sell order market pair
    Structs.Asset memory sellBaseAsset = _assetRegistry.loadAssetBySymbol(
      trade.baseAssetSymbol,
      UUID.getTimestampInMsFromUuidV1(sell.nonce)
    );
    Structs.Asset memory sellQuoteAsset = _assetRegistry.loadAssetBySymbol(
      trade.quoteAssetSymbol,
      UUID.getTimestampInMsFromUuidV1(sell.nonce)
    );
    require(
      sellBaseAsset.assetAddress == trade.baseAssetAddress &&
        sellQuoteAsset.assetAddress == trade.quoteAssetAddress,
      'Sell order market symbol address resolution mismatch'
    );

    // Fee asset validation
    require(
      trade.makerFeeAssetAddress == trade.baseAssetAddress ||
        trade.makerFeeAssetAddress == trade.quoteAssetAddress,
      'Maker fee asset is not in trade pair'
    );
    require(
      trade.takerFeeAssetAddress == trade.baseAssetAddress ||
        trade.takerFeeAssetAddress == trade.quoteAssetAddress,
      'Taker fee asset is not in trade pair'
    );
    require(
      trade.makerFeeAssetAddress != trade.takerFeeAssetAddress,
      'Maker and taker fee assets must be different'
    );
  }

  function validateLimitPrices(
    Structs.Order memory buy,
    Structs.Order memory sell,
    Structs.Trade memory trade
  ) private pure {
    require(
      trade.grossBaseQuantityInPips > 0,
      'Base quantity must be greater than zero'
    );
    require(
      trade.grossQuoteQuantityInPips > 0,
      'Quote quantity must be greater than zero'
    );

    if (isLimitOrderType(buy.orderType)) {
      require(
        getImpliedQuoteQuantityInPips(
          trade.grossBaseQuantityInPips,
          buy.limitPriceInPips
        ) >= trade.grossQuoteQuantityInPips,
        'Buy order limit price exceeded'
      );
    }

    if (isLimitOrderType(sell.orderType)) {
      require(
        getImpliedQuoteQuantityInPips(
          trade.grossBaseQuantityInPips,
          sell.limitPriceInPips
        ) <= trade.grossQuoteQuantityInPips,
        'Sell order limit price exceeded'
      );
    }
  }

  function validateTradeFees(Structs.Trade memory trade) private pure {
    uint64 makerTotalQuantityInPips = trade.makerFeeAssetAddress ==
      trade.baseAssetAddress
      ? trade.grossBaseQuantityInPips
      : trade.grossQuoteQuantityInPips;
    require(
      getFeeBasisPoints(
        trade.makerFeeQuantityInPips,
        makerTotalQuantityInPips
      ) <= _maxTradeFeeBasisPoints,
      'Excessive maker fee'
    );

    uint64 takerTotalQuantityInPips = trade.takerFeeAssetAddress ==
      trade.baseAssetAddress
      ? trade.grossBaseQuantityInPips
      : trade.grossQuoteQuantityInPips;
    require(
      getFeeBasisPoints(
        trade.takerFeeQuantityInPips,
        takerTotalQuantityInPips
      ) <= _maxTradeFeeBasisPoints,
      'Excessive taker fee'
    );

    require(
      trade.netBaseQuantityInPips.add(
        trade.makerFeeAssetAddress == trade.baseAssetAddress
          ? trade.makerFeeQuantityInPips
          : trade.takerFeeQuantityInPips
      ) == trade.grossBaseQuantityInPips,
      'Net base plus fee is not equal to gross'
    );
    require(
      trade.netQuoteQuantityInPips.add(
        trade.makerFeeAssetAddress == trade.quoteAssetAddress
          ? trade.makerFeeQuantityInPips
          : trade.takerFeeQuantityInPips
      ) == trade.grossQuoteQuantityInPips,
      'Net quote plus fee is not equal to gross'
    );
  }

  function validateOrderSignatures(
    Structs.Order memory buy,
    Structs.Order memory sell,
    Structs.Trade memory trade
  ) private pure returns (bytes32, bytes32) {
    bytes32 buyOrderHash = validateOrderSignature(buy, trade);
    bytes32 sellOrderHash = validateOrderSignature(sell, trade);

    return (buyOrderHash, sellOrderHash);
  }

  function validateOrderSignature(
    Structs.Order memory order,
    Structs.Trade memory trade
  ) private pure returns (bytes32) {
    bytes32 orderHash = Signatures.getOrderWalletHash(
      order,
      trade.baseAssetSymbol,
      trade.quoteAssetSymbol
    );

    require(
      Signatures.isSignatureValid(
        orderHash,
        order.walletSignature,
        order.walletAddress
      ),
      order.side == Enums.OrderSide.Buy
        ? 'Invalid wallet signature for buy order'
        : 'Invalid wallet signature for sell order'
    );

    return orderHash;
  }

  function validateOrderNonces(
    Structs.Order memory buy,
    Structs.Order memory sell
  ) private view {
    require(
      UUID.getTimestampInMsFromUuidV1(buy.nonce) >
        getLastInvalidatedTimestamp(buy.walletAddress),
      'Buy order nonce timestamp too low'
    );
    require(
      UUID.getTimestampInMsFromUuidV1(sell.nonce) >
        getLastInvalidatedTimestamp(sell.walletAddress),
      'Sell order nonce timestamp too low'
    );
  }

  function validateWithdrawalSignature(Structs.Withdrawal memory withdrawal)
    private
    pure
    returns (bytes32)
  {
    bytes32 withdrawalHash = Signatures.getWithdrawalWalletHash(withdrawal);

    require(
      Signatures.isSignatureValid(
        withdrawalHash,
        withdrawal.walletSignature,
        withdrawal.walletAddress
      ),
      'Invalid wallet signature'
    );

    return withdrawalHash;
  }

  // Asset registry //

  /**
   * @notice Initiate registration process for a token asset. Only `IERC20` compliant tokens can be
   * added - ETH is hardcoded in the registry
   *
   * @param tokenAddress The address of the `IERC20` compliant token contract to add
   * @param symbol The symbol identifying the token asset
   * @param decimals The decimal precision of the token
   */
  function registerToken(
    IERC20 tokenAddress,
    string calldata symbol,
    uint8 decimals
  ) external onlyAdmin {
    _assetRegistry.registerToken(tokenAddress, symbol, decimals);
    emit TokenRegistered(tokenAddress, symbol, decimals);
  }

  /**
   * @notice Finalize registration process for a token asset. All parameters must exactly match a previous
   * call to `registerToken`
   *
   * @param tokenAddress The address of the `IERC20` compliant token contract to add
   * @param symbol The symbol identifying the token asset
   * @param decimals The decimal precision of the token
   */
  function confirmTokenRegistration(
    IERC20 tokenAddress,
    string calldata symbol,
    uint8 decimals
  ) external onlyAdmin {
    _assetRegistry.confirmTokenRegistration(tokenAddress, symbol, decimals);
    emit TokenRegistrationConfirmed(tokenAddress, symbol, decimals);
  }

  /**
   * @notice Add a symbol to a token that has already been registered and confirmed
   *
   * @param tokenAddress The address of the `IERC20` compliant token contract the symbol will identify
   * @param symbol The symbol identifying the token asset
   */
  function addTokenSymbol(IERC20 tokenAddress, string calldata symbol)
    external
    onlyAdmin
  {
    _assetRegistry.addTokenSymbol(tokenAddress, symbol);
    emit TokenSymbolAdded(tokenAddress, symbol);
  }

  /**
   * @notice Loads an asset descriptor struct by its symbol and timestamp
   *
   * @dev Since multiple token addresses can potentially share the same symbol (in case of a token
   * swap/contract upgrade) the provided `timestampInMs` is compared against each asset's
   * `confirmedTimestampInMs` to uniquely determine the newest asset for the symbol at that point in time
   *
   * @param assetSymbol The asset's symbol
   * @param timestampInMs Point in time used to disambiguate multiple tokens with same symbol
   *
   * @return A `Structs.Asset` record describing the asset
   */
  function loadAssetBySymbol(string calldata assetSymbol, uint64 timestampInMs)
    external
    view
    returns (Structs.Asset memory)
  {
    return _assetRegistry.loadAssetBySymbol(assetSymbol, timestampInMs);
  }

  // Dispatcher whitelisting //

  /**
   * @notice Sets the wallet whitelisted to dispatch transactions calling the `executeTrade` and `withdraw` functions
   *
   * @param newDispatcherWallet The new whitelisted dispatcher wallet. Must be different from the current one
   */
  function setDispatcher(address newDispatcherWallet) external onlyAdmin {
    require(newDispatcherWallet != address(0x0), 'Invalid wallet address');
    require(
      newDispatcherWallet != _dispatcherWallet,
      'Must be different from current dispatcher'
    );
    address oldDispatcherWallet = _dispatcherWallet;
    _dispatcherWallet = newDispatcherWallet;

    emit DispatcherChanged(oldDispatcherWallet, newDispatcherWallet);
  }

  /**
   * @notice Clears the currently set whitelisted dispatcher wallet, effectively disabling calling the
   * `executeTrade` and `withdraw` functions until a new wallet is set with `setDispatcher`
   */
  function removeDispatcher() external onlyAdmin {
    emit DispatcherChanged(_dispatcherWallet, address(0x0));
    _dispatcherWallet = address(0x0);
  }

  modifier onlyDispatcher() {
    require(msg.sender == _dispatcherWallet, 'Caller is not dispatcher');
    _;
  }

  // Utils //

  function isLimitOrderType(Enums.OrderType orderType)
    private
    pure
    returns (bool)
  {
    return
      orderType == Enums.OrderType.Limit ||
      orderType == Enums.OrderType.LimitMaker ||
      orderType == Enums.OrderType.StopLossLimit ||
      orderType == Enums.OrderType.TakeProfitLimit;
  }

  function isMarketOrderType(Enums.OrderType orderType)
    private
    pure
    returns (bool)
  {
    return
      orderType == Enums.OrderType.Market ||
      orderType == Enums.OrderType.StopLoss ||
      orderType == Enums.OrderType.TakeProfit;
  }

  function getCurrentTimestampInMs() private view returns (uint64) {
    uint64 msInOneSecond = 1000;

    return uint64(block.timestamp) * msInOneSecond;
  }

  function getFeeBasisPoints(uint64 fee, uint64 total)
    private
    pure
    returns (uint64)
  {
    uint64 basisPointsInTotal = 100 * 100; // 100 basis points/percent * 100 percent/total
    return fee.mul(basisPointsInTotal).div(total);
  }

  function getImpliedQuoteQuantityInPips(
    uint64 baseQuantityInPips,
    uint64 limitPriceInPips
  ) private pure returns (uint64) {
    // To convert a fractional price to integer pips, shift right by the pip precision of 8 decimals
    uint256 pipsMultiplier = 10**8;

    uint256 impliedQuoteQuantityInPips = uint256(baseQuantityInPips)
      .mul(uint256(limitPriceInPips))
      .div(pipsMultiplier);
    require(
      impliedQuoteQuantityInPips < 2**64,
      'Implied quote pip quantity overflows uint64'
    );

    return uint64(impliedQuoteQuantityInPips);
  }

  function getLastInvalidatedTimestamp(address walletAddress)
    private
    view
    returns (uint64)
  {
    if (
      _nonceInvalidations[walletAddress].exists &&
      _nonceInvalidations[walletAddress].effectiveBlockNumber <= block.number
    ) {
      return _nonceInvalidations[walletAddress].timestampInMs;
    }

    return 0;
  }

  function getOneDayFromNowInMs() private view returns (uint64) {
    uint64 secondsInOneDay = 24 * 60 * 60; // 24 hours/day * 60 min/hour * 60 seconds/min
    uint64 msInOneSecond = 1000;

    return (uint64(block.timestamp) + secondsInOneDay) * msInOneSecond;
  }
}

File 2 of 12: Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.8;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 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");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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 functionCall(target, data, "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");
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
        if (success) {
            return returndata;
        } else {
            // 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 3 of 12: AssetRegistry.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

import { Address } from './Address.sol';

import { IERC20, Structs } from './Interfaces.sol';


/**
 * @notice Library helper functions for managing a registry of asset descriptors indexed by address and symbol
 */
library AssetRegistry {
  struct Storage {
    mapping(address => Structs.Asset) assetsByAddress;
    // Mapping value is array since the same symbol can be re-used for a different address
    // (usually as a result of a token swap or upgrade)
    mapping(string => Structs.Asset[]) assetsBySymbol;
  }

  function registerToken(
    Storage storage self,
    IERC20 tokenAddress,
    string memory symbol,
    uint8 decimals
  ) internal {
    require(decimals <= 32, 'Token cannot have more than 32 decimals');
    require(
      tokenAddress != IERC20(0x0) && Address.isContract(address(tokenAddress)),
      'Invalid token address'
    );
    // The string type does not have a length property so cast to bytes to check for empty string
    require(bytes(symbol).length > 0, 'Invalid token symbol');
    require(
      !self.assetsByAddress[address(tokenAddress)].isConfirmed,
      'Token already finalized'
    );

    self.assetsByAddress[address(tokenAddress)] = Structs.Asset({
      exists: true,
      assetAddress: address(tokenAddress),
      symbol: symbol,
      decimals: decimals,
      isConfirmed: false,
      confirmedTimestampInMs: 0
    });
  }

  function confirmTokenRegistration(
    Storage storage self,
    IERC20 tokenAddress,
    string memory symbol,
    uint8 decimals
  ) internal {
    Structs.Asset memory asset = self.assetsByAddress[address(tokenAddress)];
    require(asset.exists, 'Unknown token');
    require(!asset.isConfirmed, 'Token already finalized');
    require(isStringEqual(asset.symbol, symbol), 'Symbols do not match');
    require(asset.decimals == decimals, 'Decimals do not match');

    asset.isConfirmed = true;
    asset.confirmedTimestampInMs = uint64(block.timestamp * 1000); // Block timestamp is in seconds, store ms
    self.assetsByAddress[address(tokenAddress)] = asset;
    self.assetsBySymbol[symbol].push(asset);
  }

  function addTokenSymbol(
    Storage storage self,
    IERC20 tokenAddress,
    string memory symbol
  ) internal {
    Structs.Asset memory asset = self.assetsByAddress[address(tokenAddress)];
    require(
      asset.exists && asset.isConfirmed,
      'Registration of token not finalized'
    );
    require(!isStringEqual(symbol, 'ETH'), 'ETH symbol reserved for Ether');

    // This will prevent swapping assets for previously existing orders
    uint64 msInOneSecond = 1000;
    asset.confirmedTimestampInMs = uint64(block.timestamp * msInOneSecond);

    self.assetsBySymbol[symbol].push(asset);
  }

  /**
   * @dev Resolves an asset address into corresponding Asset struct
   *
   * @param assetAddress Ethereum address of asset
   */
  function loadAssetByAddress(Storage storage self, address assetAddress)
    internal
    view
    returns (Structs.Asset memory)
  {
    if (assetAddress == address(0x0)) {
      return getEthAsset();
    }

    Structs.Asset memory asset = self.assetsByAddress[assetAddress];
    require(
      asset.exists && asset.isConfirmed,
      'No confirmed asset found for address'
    );

    return asset;
  }

  /**
   * @dev Resolves a asset symbol into corresponding Asset struct
   *
   * @param symbol Asset symbol, e.g. 'IDEX'
   * @param timestampInMs Milliseconds since Unix epoch, usually parsed from a UUID v1 order nonce.
   * Constrains symbol resolution to the asset most recently confirmed prior to timestampInMs. Reverts
   * if no such asset exists
   */
  function loadAssetBySymbol(
    Storage storage self,
    string memory symbol,
    uint64 timestampInMs
  ) internal view returns (Structs.Asset memory) {
    if (isStringEqual('ETH', symbol)) {
      return getEthAsset();
    }

    Structs.Asset memory asset;
    if (self.assetsBySymbol[symbol].length > 0) {
      for (uint8 i = 0; i < self.assetsBySymbol[symbol].length; i++) {
        if (
          self.assetsBySymbol[symbol][i].confirmedTimestampInMs <= timestampInMs
        ) {
          asset = self.assetsBySymbol[symbol][i];
        }
      }
    }
    require(
      asset.exists && asset.isConfirmed,
      'No confirmed asset found for symbol'
    );

    return asset;
  }

  /**
   * @dev ETH is modeled as an always-confirmed Asset struct for programmatic consistency
   */
  function getEthAsset() private pure returns (Structs.Asset memory) {
    return Structs.Asset(true, address(0x0), 'ETH', 18, true, 0);
  }

  // See https://solidity.readthedocs.io/en/latest/types.html#bytes-and-strings-as-arrays
  function isStringEqual(string memory a, string memory b)
    private
    pure
    returns (bool)
  {
    return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
  }
}

File 4 of 12: AssetTransfers.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

import {
  SafeMath as SafeMath256
} from './SafeMath.sol';

import { IERC20 } from './Interfaces.sol';


/**
 * @notice This library provides helper utilities for transfering assets in and out of contracts.
 * It further validates ERC-20 compliant balance updates in the case of token assets
 */
library AssetTransfers {
  using SafeMath256 for uint256;

  /**
   * @dev Transfers tokens from a wallet into a contract during deposits. `wallet` must already
   * have called `approve` on the token contract for at least `tokenQuantity`. Note this only
   * applies to tokens since ETH is sent in the deposit transaction via `msg.value`
   */
  function transferFrom(
    address wallet,
    IERC20 tokenAddress,
    uint256 quantityInAssetUnits
  ) internal {
    uint256 balanceBefore = tokenAddress.balanceOf(address(this));

    // Because we check for the expected balance change we can safely ignore the return value of transferFrom
    tokenAddress.transferFrom(wallet, address(this), quantityInAssetUnits);

    uint256 balanceAfter = tokenAddress.balanceOf(address(this));
    require(
      balanceAfter.sub(balanceBefore) == quantityInAssetUnits,
      'Token contract returned transferFrom success without expected balance change'
    );
  }

  /**
   * @dev Transfers ETH or token assets from a contract to 1) another contract, when `Exchange`
   * forwards funds to `Custodian` during deposit or 2) a wallet, when withdrawing
   */
  function transferTo(
    address payable walletOrContract,
    address asset,
    uint256 quantityInAssetUnits
  ) internal {
    if (asset == address(0x0)) {
      require(
        walletOrContract.send(quantityInAssetUnits),
        'ETH transfer failed'
      );
    } else {
      uint256 balanceBefore = IERC20(asset).balanceOf(walletOrContract);

      // Because we check for the expected balance change we can safely ignore the return value of transfer
      IERC20(asset).transfer(walletOrContract, quantityInAssetUnits);

      uint256 balanceAfter = IERC20(asset).balanceOf(walletOrContract);
      require(
        balanceAfter.sub(balanceBefore) == quantityInAssetUnits,
        'Token contract returned transfer success without expected balance change'
      );
    }
  }
}

File 5 of 12: AssetUnitConversions.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

import {
  SafeMath as SafeMath256
} from './SafeMath.sol';


/**
 * @notice Library helpers for converting asset quantities between asset units and pips
 */
library AssetUnitConversions {
  using SafeMath256 for uint256;

  function pipsToAssetUnits(uint64 quantityInPips, uint8 assetDecimals)
    internal
    pure
    returns (uint256)
  {
    require(assetDecimals <= 32, 'Asset cannot have more than 32 decimals');

    // Exponents cannot be negative, so divide or multiply based on exponent signedness
    if (assetDecimals > 8) {
      return uint256(quantityInPips).mul(uint256(10)**(assetDecimals - 8));
    }
    return uint256(quantityInPips).div(uint256(10)**(8 - assetDecimals));
  }

  function assetUnitsToPips(uint256 quantityInAssetUnits, uint8 assetDecimals)
    internal
    pure
    returns (uint64)
  {
    require(assetDecimals <= 32, 'Asset cannot have more than 32 decimals');

    uint256 quantityInPips;
    // Exponents cannot be negative, so divide or multiply based on exponent signedness
    if (assetDecimals > 8) {
      quantityInPips = quantityInAssetUnits.div(
        uint256(10)**(assetDecimals - 8)
      );
    } else {
      quantityInPips = quantityInAssetUnits.mul(
        uint256(10)**(8 - assetDecimals)
      );
    }
    require(quantityInPips < 2**64, 'Pip quantity overflows uint64');

    return uint64(quantityInPips);
  }
}

File 6 of 12: ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.8;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            revert("ECDSA: invalid signature 's' value");
        }

        if (v != 27 && v != 28) {
            revert("ECDSA: invalid signature 'v' value");
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}

File 7 of 12: Interfaces.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;


/**
 * @notice Enums used in `Order` and `Withdrawal` structs
 */
contract Enums {
  enum OrderSelfTradePrevention {
    // Decrement and cancel
    dc,
    // Cancel oldest
    co,
    // Cancel newest
    cn,
    // Cancel both
    cb
  }
  enum OrderSide { Buy, Sell }
  enum OrderTimeInForce {
    // Good until cancelled
    gtc,
    // Good until time
    gtt,
    // Immediate or cancel
    ioc,
    // Fill or kill
    fok
  }
  enum OrderType {
    Market,
    Limit,
    LimitMaker,
    StopLoss,
    StopLossLimit,
    TakeProfit,
    TakeProfitLimit
  }
  enum WithdrawalType { BySymbol, ByAddress }
}


/**
 * @notice Struct definitions
 */
contract Structs {
  /**
   * @notice Argument type for `Exchange.executeTrade` and `Signatures.getOrderWalletHash`
   */
  struct Order {
    // Not currently used but reserved for future use. Must be 1
    uint8 signatureHashVersion;
    // UUIDv1 unique to wallet
    uint128 nonce;
    // Wallet address that placed order and signed hash
    address walletAddress;
    // Type of order
    Enums.OrderType orderType;
    // Order side wallet is on
    Enums.OrderSide side;
    // Order quantity in base or quote asset terms depending on isQuantityInQuote flag
    uint64 quantityInPips;
    // Is quantityInPips in quote terms
    bool isQuantityInQuote;
    // For limit orders, price in decimal pips * 10^8 in quote terms
    uint64 limitPriceInPips;
    // For stop orders, stop loss or take profit price in decimal pips * 10^8 in quote terms
    uint64 stopPriceInPips;
    // Optional custom client order ID
    string clientOrderId;
    // TIF option specified by wallet for order
    Enums.OrderTimeInForce timeInForce;
    // STP behavior specified by wallet for order
    Enums.OrderSelfTradePrevention selfTradePrevention;
    // Cancellation time specified by wallet for GTT TIF order
    uint64 cancelAfter;
    // The ECDSA signature of the order hash as produced by Signatures.getOrderWalletHash
    bytes walletSignature;
  }

  /**
   * @notice Return type for `Exchange.loadAssetBySymbol`, and `Exchange.loadAssetByAddress`; also
   * used internally by `AssetRegistry`
   */
  struct Asset {
    // Flag to distinguish from empty struct
    bool exists;
    // The asset's address
    address assetAddress;
    // The asset's symbol
    string symbol;
    // The asset's decimal precision
    uint8 decimals;
    // Flag set when asset registration confirmed. Asset deposits, trades, or withdrawals only allowed if true
    bool isConfirmed;
    // Timestamp as ms since Unix epoch when isConfirmed was asserted
    uint64 confirmedTimestampInMs;
  }

  /**
   * @notice Argument type for `Exchange.executeTrade` specifying execution parameters for matching orders
   */
  struct Trade {
    // Base asset symbol
    string baseAssetSymbol;
    // Quote asset symbol
    string quoteAssetSymbol;
    // Base asset address
    address baseAssetAddress;
    // Quote asset address
    address quoteAssetAddress;
    // Gross amount including fees of base asset executed
    uint64 grossBaseQuantityInPips;
    // Gross amount including fees of quote asset executed
    uint64 grossQuoteQuantityInPips;
    // Net amount of base asset received by buy side wallet after fees
    uint64 netBaseQuantityInPips;
    // Net amount of quote asset received by sell side wallet after fees
    uint64 netQuoteQuantityInPips;
    // Asset address for liquidity maker's fee
    address makerFeeAssetAddress;
    // Asset address for liquidity taker's fee
    address takerFeeAssetAddress;
    // Fee paid by liquidity maker
    uint64 makerFeeQuantityInPips;
    // Fee paid by liquidity taker
    uint64 takerFeeQuantityInPips;
    // Execution price of trade in decimal pips * 10^8 in quote terms
    uint64 priceInPips;
    // Which side of the order (buy or sell) the liquidity maker was on
    Enums.OrderSide makerSide;
  }

  /**
   * @notice Argument type for `Exchange.withdraw` and `Signatures.getWithdrawalWalletHash`
   */
  struct Withdrawal {
    // Distinguishes between withdrawals by asset symbol or address
    Enums.WithdrawalType withdrawalType;
    // UUIDv1 unique to wallet
    uint128 nonce;
    // Address of wallet to which funds will be returned
    address payable walletAddress;
    // Asset symbol
    string assetSymbol;
    // Asset address
    address assetAddress; // Used when assetSymbol not specified
    // Withdrawal quantity
    uint64 quantityInPips;
    // Gas fee deducted from withdrawn quantity to cover dispatcher tx costs
    uint64 gasFeeInPips;
    // Not currently used but reserved for future use. Must be true
    bool autoDispatchEnabled;
    // The ECDSA signature of the withdrawal hash as produced by Signatures.getWithdrawalWalletHash
    bytes walletSignature;
  }
}


/**
 * @notice Interface of the ERC20 standard as defined in the EIP, but with no return values for
 * transfer and transferFrom. By asserting expected balance changes when calling these two methods
 * we can safely ignore their return values. This allows support of non-compliant tokens that do not
 * return a boolean. See https://github.com/ethereum/solidity/issues/4116
 */
interface IERC20 {
  /**
   * @notice Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

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

  /**
   * @notice Moves `amount` tokens from the caller's account to `recipient`.
   *
   * Most implementing contracts return a boolean value indicating whether the operation succeeded, but
   * we ignore this and rely on asserting balance changes instead
   *
   * Emits a {Transfer} event.
   */
  function transfer(address recipient, uint256 amount) external;

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

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

  /**
   * @notice Moves `amount` tokens from `sender` to `recipient` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Most implementing contracts return a boolean value indicating whether the operation succeeded, but
   * we ignore this and rely on asserting balance changes instead
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) external;

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

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


/**
 * @notice Interface to Custodian contract. Used by Exchange and Governance contracts for internal
 * delegate calls
 */
interface ICustodian {
  /**
   * @notice ETH can only be sent by the Exchange
   */
  receive() external payable;

  /**
   * @notice Withdraw any asset and amount to a target wallet
   *
   * @dev No balance checking performed
   *
   * @param wallet The wallet to which assets will be returned
   * @param asset The address of the asset to withdraw (ETH or ERC-20 contract)
   * @param quantityInAssetUnits The quantity in asset units to withdraw
   */
  function withdraw(
    address payable wallet,
    address asset,
    uint256 quantityInAssetUnits
  ) external;

  /**
   * @notice Load address of the currently whitelisted Exchange contract
   *
   * @return The address of the currently whitelisted Exchange contract
   */
  function loadExchange() external view returns (address);

  /**
   * @notice Sets a new Exchange contract address
   *
   * @param newExchange The address of the new whitelisted Exchange contract
   */
  function setExchange(address newExchange) external;

  /**
   * @notice Load address of the currently whitelisted Governance contract
   *
   * @return The address of the currently whitelisted Governance contract
   */
  function loadGovernance() external view returns (address);

  /**
   * @notice Sets a new Governance contract address
   *
   * @param newGovernance The address of the new whitelisted Governance contract
   */
  function setGovernance(address newGovernance) external;
}


/**
 * @notice Interface to Exchange contract. Provided only to document struct usage
 */
interface IExchange {
  /**
   * @notice Settles a trade between two orders submitted and matched off-chain
   *
   * @param buy A `Structs.Order` struct encoding the parameters of the buy-side order (receiving base, giving quote)
   * @param sell A `Structs.Order` struct encoding the parameters of the sell-side order (giving base, receiving quote)
   * @param trade A `Structs.Trade` struct encoding the parameters of this trade execution of the counterparty orders
   */
  function executeTrade(
    Structs.Order calldata buy,
    Structs.Order calldata sell,
    Structs.Trade calldata trade
  ) external;

  /**
   * @notice Settles a user withdrawal submitted off-chain. Calls restricted to currently whitelisted Dispatcher wallet
   *
   * @param withdrawal A `Structs.Withdrawal` struct encoding the parameters of the withdrawal
   */
  function withdraw(Structs.Withdrawal calldata withdrawal) external;
}

File 8 of 12: Owned.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;


/**
 * @notice Mixin that provide separate owner and admin roles for RBAC
 */
abstract contract Owned {
  address immutable _owner;
  address _admin;

  modifier onlyOwner {
    require(msg.sender == _owner, 'Caller must be owner');
    _;
  }
  modifier onlyAdmin {
    require(msg.sender == _admin, 'Caller must be admin');
    _;
  }

  /**
   * @notice Sets both the owner and admin roles to the contract creator
   */
  constructor() public {
    _owner = msg.sender;
    _admin = msg.sender;
  }

  /**
   * @notice Sets a new whitelisted admin wallet
   *
   * @param newAdmin The new whitelisted admin wallet. Must be different from the current one
   */
  function setAdmin(address newAdmin) external onlyOwner {
    require(newAdmin != address(0x0), 'Invalid wallet address');
    require(newAdmin != _admin, 'Must be different from current admin');

    _admin = newAdmin;
  }

  /**
   * @notice Clears the currently whitelisted admin wallet, effectively disabling any functions requiring
   * the admin role
   */
  function removeAdmin() external onlyOwner {
    _admin = address(0x0);
  }
}

File 9 of 12: SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.8;

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

        return c;
    }

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

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

File 10 of 12: SafeMath64.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;


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

    return c;
  }

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

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

    return c;
  }

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

    uint64 c = a * b;
    require(c / a == b, 'SafeMath: multiplication overflow');

    return c;
  }

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

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

    return c;
  }
}

File 11 of 12: Signatures.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

import { ECDSA } from './ECDSA.sol';

import { Enums, Structs } from './Interfaces.sol';


/**
 * Library helpers for building hashes and verifying wallet signatures on `Order` and `Withdrawal` structs
 */
library Signatures {
  function isSignatureValid(
    bytes32 hash,
    bytes memory signature,
    address signer
  ) internal pure returns (bool) {
    return
      ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), signature) == signer;
  }

  function getOrderWalletHash(
    Structs.Order memory order,
    string memory baseSymbol,
    string memory quoteSymbol
  ) internal pure returns (bytes32) {
    require(
      order.signatureHashVersion == 1,
      'Signature hash version must be 1'
    );
    return
      keccak256(
        // Placing all the fields in a single `abi.encodePacked` call causes a `stack too deep` error
        abi.encodePacked(
          abi.encodePacked(
            order.signatureHashVersion,
            order.nonce,
            order.walletAddress,
            getMarketSymbol(baseSymbol, quoteSymbol),
            uint8(order.orderType),
            uint8(order.side),
            // Ledger qtys and prices are in pip, but order was signed by wallet owner with decimal values
            pipToDecimal(order.quantityInPips)
          ),
          abi.encodePacked(
            order.isQuantityInQuote,
            order.limitPriceInPips > 0
              ? pipToDecimal(order.limitPriceInPips)
              : '',
            order.stopPriceInPips > 0
              ? pipToDecimal(order.stopPriceInPips)
              : '',
            order.clientOrderId,
            uint8(order.timeInForce),
            uint8(order.selfTradePrevention),
            order.cancelAfter
          )
        )
      );
  }

  function getWithdrawalWalletHash(Structs.Withdrawal memory withdrawal)
    internal
    pure
    returns (bytes32)
  {
    return
      keccak256(
        abi.encodePacked(
          withdrawal.nonce,
          withdrawal.walletAddress,
          // Ternary branches must resolve to the same type, so wrap in idempotent encodePacked
          withdrawal.withdrawalType == Enums.WithdrawalType.BySymbol
            ? abi.encodePacked(withdrawal.assetSymbol)
            : abi.encodePacked(withdrawal.assetAddress),
          pipToDecimal(withdrawal.quantityInPips),
          withdrawal.autoDispatchEnabled
        )
      );
  }

  /**
   * @dev Combines base and quote asset symbols into the market symbol originally signed by the
   * wallet. For example if base is 'IDEX' and quote is 'ETH', the resulting market symbol is
   * 'IDEX-ETH'. This approach is used rather than passing in the market symbol and splitting it
   * since the latter incurs a higher gas cost
   */
  function getMarketSymbol(string memory baseSymbol, string memory quoteSymbol)
    private
    pure
    returns (string memory)
  {
    bytes memory baseSymbolBytes = bytes(baseSymbol);
    bytes memory hyphenBytes = bytes('-');
    bytes memory quoteSymbolBytes = bytes(quoteSymbol);

    bytes memory marketSymbolBytes = bytes(
      new string(
        baseSymbolBytes.length + quoteSymbolBytes.length + hyphenBytes.length
      )
    );

    uint256 i;
    uint256 j;

    for (i = 0; i < baseSymbolBytes.length; i++) {
      marketSymbolBytes[j++] = baseSymbolBytes[i];
    }

    // Hyphen is one byte
    marketSymbolBytes[j++] = hyphenBytes[0];

    for (i = 0; i < quoteSymbolBytes.length; i++) {
      marketSymbolBytes[j++] = quoteSymbolBytes[i];
    }

    return string(marketSymbolBytes);
  }

  /**
   * @dev Converts an integer pip quantity back into the fixed-precision decimal pip string
   * originally signed by the wallet. For example, 1234567890 becomes '12.34567890'
   */
  function pipToDecimal(uint256 pips) private pure returns (string memory) {
    // Inspired by https://github.com/provable-things/ethereum-api/blob/831f4123816f7a3e57ebea171a3cdcf3b528e475/oraclizeAPI_0.5.sol#L1045-L1062
    uint256 copy = pips;
    uint256 length;
    while (copy != 0) {
      length++;
      copy /= 10;
    }
    if (length < 9) {
      length = 9; // a zero before the decimal point plus 8 decimals
    }
    length++; // for the decimal point

    bytes memory decimal = new bytes(length);
    for (uint256 i = length; i > 0; i--) {
      if (length - i == 8) {
        decimal[i - 1] = bytes1(uint8(46)); // period
      } else {
        decimal[i - 1] = bytes1(uint8(48 + (pips % 10)));
        pips /= 10;
      }
    }
    return string(decimal);
  }
}

File 12 of 12: UUID.sol
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.6.8;

import { SafeMath64 } from './SafeMath64.sol';


/**
 * Library helper for extracting timestamp component of Version 1 UUIDs
 */
library UUID {
  using SafeMath64 for uint64;

  /**
   * Extracts the timestamp component of a Version 1 UUID. Used to make time-based assertions
   * against a wallet-privided nonce
   */
  function getTimestampInMsFromUuidV1(uint128 uuid)
    internal
    pure
    returns (uint64 msSinceUnixEpoch)
  {
    // https://tools.ietf.org/html/rfc4122#section-4.1.2
    uint128 version = (uuid >> 76) & 0x0000000000000000000000000000000F;
    require(version == 1, 'Must be v1 UUID');

    // Time components are in reverse order so shift+mask each to reassemble
    uint128 timeHigh = (uuid >> 16) & 0x00000000000000000FFF000000000000;
    uint128 timeMid = (uuid >> 48) & 0x00000000000000000000FFFF00000000;
    uint128 timeLow = (uuid >> 96) & 0x000000000000000000000000FFFFFFFF;
    uint128 nsSinceGregorianEpoch = (timeHigh | timeMid | timeLow);
    // Gregorian offset given in seconds by https://www.wolframalpha.com/input/?i=convert+1582-10-15+UTC+to+unix+time
    msSinceUnixEpoch = uint64(nsSinceGregorianEpoch / 10000).sub(
      12219292800000
    );

    return msSinceUnixEpoch;
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"ChainPropagationPeriodChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"index","type":"uint64"},{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":true,"internalType":"string","name":"assetSymbolIndex","type":"string"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"},{"indexed":false,"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newExchangeBalanceInPips","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"newExchangeBalanceInAssetUnits","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousValue","type":"address"},{"indexed":false,"internalType":"address","name":"newValue","type":"address"}],"name":"DispatcherChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousValue","type":"address"},{"indexed":false,"internalType":"address","name":"newValue","type":"address"}],"name":"FeeWalletChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint128","name":"nonce","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"timestampInMs","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"effectiveBlockNumber","type":"uint256"}],"name":"OrderNonceInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"TokenRegistrationConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"}],"name":"TokenSymbolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"buyWallet","type":"address"},{"indexed":false,"internalType":"address","name":"sellWallet","type":"address"},{"indexed":true,"internalType":"string","name":"baseAssetSymbolIndex","type":"string"},{"indexed":true,"internalType":"string","name":"quoteAssetSymbolIndex","type":"string"},{"indexed":false,"internalType":"string","name":"baseAssetSymbol","type":"string"},{"indexed":false,"internalType":"string","name":"quoteAssetSymbol","type":"string"},{"indexed":false,"internalType":"uint64","name":"baseQuantityInPips","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"quoteQuantityInPips","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"tradePriceInPips","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"buyOrderHash","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"sellOrderHash","type":"bytes32"}],"name":"TradeExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"WalletExitCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"},{"indexed":false,"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newExchangeBalanceInPips","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"newExchangeBalanceInAssetUnits","type":"uint256"}],"name":"WalletExitWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"effectiveBlockNumber","type":"uint256"}],"name":"WalletExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"string","name":"assetSymbol","type":"string"},{"indexed":false,"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newExchangeBalanceInPips","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"newExchangeBalanceInAssetUnits","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"string","name":"symbol","type":"string"}],"name":"addTokenSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearWalletExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"confirmTokenRegistration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositEther","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"quantityInAssetUnits","type":"uint256"}],"name":"depositTokenByAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"assetSymbol","type":"string"},{"internalType":"uint256","name":"quantityInAssetUnits","type":"uint256"}],"name":"depositTokenBySymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"signatureHashVersion","type":"uint8"},{"internalType":"uint128","name":"nonce","type":"uint128"},{"internalType":"address","name":"walletAddress","type":"address"},{"internalType":"enum Enums.OrderType","name":"orderType","type":"uint8"},{"internalType":"enum Enums.OrderSide","name":"side","type":"uint8"},{"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"internalType":"bool","name":"isQuantityInQuote","type":"bool"},{"internalType":"uint64","name":"limitPriceInPips","type":"uint64"},{"internalType":"uint64","name":"stopPriceInPips","type":"uint64"},{"internalType":"string","name":"clientOrderId","type":"string"},{"internalType":"enum Enums.OrderTimeInForce","name":"timeInForce","type":"uint8"},{"internalType":"enum Enums.OrderSelfTradePrevention","name":"selfTradePrevention","type":"uint8"},{"internalType":"uint64","name":"cancelAfter","type":"uint64"},{"internalType":"bytes","name":"walletSignature","type":"bytes"}],"internalType":"struct Structs.Order","name":"buy","type":"tuple"},{"components":[{"internalType":"uint8","name":"signatureHashVersion","type":"uint8"},{"internalType":"uint128","name":"nonce","type":"uint128"},{"internalType":"address","name":"walletAddress","type":"address"},{"internalType":"enum Enums.OrderType","name":"orderType","type":"uint8"},{"internalType":"enum Enums.OrderSide","name":"side","type":"uint8"},{"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"internalType":"bool","name":"isQuantityInQuote","type":"bool"},{"internalType":"uint64","name":"limitPriceInPips","type":"uint64"},{"internalType":"uint64","name":"stopPriceInPips","type":"uint64"},{"internalType":"string","name":"clientOrderId","type":"string"},{"internalType":"enum Enums.OrderTimeInForce","name":"timeInForce","type":"uint8"},{"internalType":"enum Enums.OrderSelfTradePrevention","name":"selfTradePrevention","type":"uint8"},{"internalType":"uint64","name":"cancelAfter","type":"uint64"},{"internalType":"bytes","name":"walletSignature","type":"bytes"}],"internalType":"struct Structs.Order","name":"sell","type":"tuple"},{"components":[{"internalType":"string","name":"baseAssetSymbol","type":"string"},{"internalType":"string","name":"quoteAssetSymbol","type":"string"},{"internalType":"address","name":"baseAssetAddress","type":"address"},{"internalType":"address","name":"quoteAssetAddress","type":"address"},{"internalType":"uint64","name":"grossBaseQuantityInPips","type":"uint64"},{"internalType":"uint64","name":"grossQuoteQuantityInPips","type":"uint64"},{"internalType":"uint64","name":"netBaseQuantityInPips","type":"uint64"},{"internalType":"uint64","name":"netQuoteQuantityInPips","type":"uint64"},{"internalType":"address","name":"makerFeeAssetAddress","type":"address"},{"internalType":"address","name":"takerFeeAssetAddress","type":"address"},{"internalType":"uint64","name":"makerFeeQuantityInPips","type":"uint64"},{"internalType":"uint64","name":"takerFeeQuantityInPips","type":"uint64"},{"internalType":"uint64","name":"priceInPips","type":"uint64"},{"internalType":"enum Enums.OrderSide","name":"makerSide","type":"uint8"}],"internalType":"struct Structs.Trade","name":"trade","type":"tuple"}],"name":"executeTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exitWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"nonce","type":"uint128"}],"name":"invalidateOrderNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"assetSymbol","type":"string"},{"internalType":"uint64","name":"timestampInMs","type":"uint64"}],"name":"loadAssetBySymbol","outputs":[{"components":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"bool","name":"isConfirmed","type":"bool"},{"internalType":"uint64","name":"confirmedTimestampInMs","type":"uint64"}],"internalType":"struct Structs.Asset","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"assetAddress","type":"address"}],"name":"loadBalanceInAssetUnitsByAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"string","name":"assetSymbol","type":"string"}],"name":"loadBalanceInAssetUnitsBySymbol","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"assetAddress","type":"address"}],"name":"loadBalanceInPipsByAddress","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"string","name":"assetSymbol","type":"string"}],"name":"loadBalanceInPipsBySymbol","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loadFeeWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"loadPartiallyFilledOrderQuantityInPips","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"registerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeDispatcher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newChainPropagationPeriod","type":"uint256"}],"name":"setChainPropagationPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newCustodian","type":"address"}],"name":"setCustodian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDispatcherWallet","type":"address"}],"name":"setDispatcher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeWallet","type":"address"}],"name":"setFeeWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum Enums.WithdrawalType","name":"withdrawalType","type":"uint8"},{"internalType":"uint128","name":"nonce","type":"uint128"},{"internalType":"address payable","name":"walletAddress","type":"address"},{"internalType":"string","name":"assetSymbol","type":"string"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint64","name":"quantityInPips","type":"uint64"},{"internalType":"uint64","name":"gasFeeInPips","type":"uint64"},{"internalType":"bool","name":"autoDispatchEnabled","type":"bool"},{"internalType":"bytes","name":"walletSignature","type":"bytes"}],"internalType":"struct Structs.Withdrawal","name":"withdrawal","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"}],"name":"withdrawExit","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561001057600080fd5b5033606081901b608052600080546001600160a01b03191682178155615ff99062000049903980610f2952806113685250615ff96000f3fe6080604052600436106101415760003560e01c806372e8f08d116100b157806372e8f08d146102ea578063869af2121461030a57806390d49b9d14610337578063985c4af51461035757806398ea5fca146103775780639a202d471461037f578063ae0e969e14610394578063ba22bd76146103c1578063d7a6aec7146103e1578063dbb3653514610401578063dcc6349014610421578063eb5068f214610441578063ef3b9d4a1461045657610141565b80630226b70e1461014657806302ca6002146101685780630c187a721461019357806313cfda2c146101a85780631abb5832146101d557806320e6a8e3146101f5578063403f3731146102155780634196818214610235578063457aa3c6146102555780634a284ef91461027557806365fce5b71461028a5780636bb509f2146102aa578063704b6c02146102ca575b600080fd5b34801561015257600080fd5b50610166610161366004614559565b610476565b005b34801561017457600080fd5b5061017d610540565b60405161018a9190614c10565b60405180910390f35b34801561019f57600080fd5b50610166610550565b3480156101b457600080fd5b506101c86101c33660046144b7565b6105d4565b60405161018a9190615ea2565b3480156101e157600080fd5b506101c86101f03660046144ef565b610662565b34801561020157600080fd5b50610166610210366004614541565b610736565b34801561022157600080fd5b5061016661023036600461449b565b6107c7565b34801561024157600080fd5b506101666102503660046145e9565b610861565b34801561026157600080fd5b50610166610270366004614559565b6108e3565b34801561028157600080fd5b50610166610996565b34801561029657600080fd5b506101666102a536600461468d565b610a0a565b3480156102b657600080fd5b506101666102c5366004614850565b610bb1565b3480156102d657600080fd5b506101666102e536600461449b565b610f1e565b3480156102f657600080fd5b506101666103053660046144ef565b610fdc565b34801561031657600080fd5b5061032a610325366004614632565b611099565b60405161018a9190615e07565b34801561034357600080fd5b5061016661035236600461449b565b6110f3565b34801561036357600080fd5b5061016661037236600461449b565b6111c4565b61016661134f565b34801561038b57600080fd5b5061016661135d565b3480156103a057600080fd5b506103b46103af366004614541565b6113b7565b60405161018a9190615eb9565b3480156103cd57600080fd5b506101666103dc36600461449b565b6113d5565b3480156103ed57600080fd5b506101666103fc366004614962565b6114a6565b34801561040d57600080fd5b506103b461041c3660046144b7565b61161e565b34801561042d57600080fd5b5061016661043c3660046145be565b61167b565b34801561044d57600080fd5b506101666116b0565b34801561046257600080fd5b506103b46104713660046144ef565b611762565b6000546001600160a01b031633146104a95760405162461bcd60e51b81526004016104a0906159ba565b60405180910390fd5b6104f58484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff61180c16565b836001600160a01b03167fa8968236dc4afb2c189afeb69c91b02445e6bd09f0ffe27256b62838edc99edb84848460405161053293929190614d25565b60405180910390a250505050565b600c546001600160a01b03165b90565b6000546001600160a01b0316331461057a5760405162461bcd60e51b81526004016104a0906159ba565b600b546040517fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39916105ba916001600160a01b0390911690600090614c61565b60405180910390a1600b80546001600160a01b0319169055565b60006001600160a01b0383166105fc5760405162461bcd60e51b81526004016104a090615c07565b610604614140565b61061560018463ffffffff611b6216565b6001600160a01b038086166000908152600660209081526040808320938816835292905220546060820151919250610658916001600160401b0390911690611cbb565b9150505b92915050565b60006001600160a01b03841661068a5760405162461bcd60e51b81526004016104a090615c07565b610692614140565b6106e484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6001919063ffffffff611d3b16565b6001600160a01b0380871660009081526006602090815260408083208286015190941683529290522054606082015191925061072b916001600160401b0390911690611cbb565b9150505b9392505050565b6000546001600160a01b031633146107605760405162461bcd60e51b81526004016104a0906159ba565b619d8081106107815760405162461bcd60e51b81526004016104a0906155ab565b600a8054908290556040517f9a22227d6c0251a79ef8b846202ddcbe9d682ee5482e84abeec6dda096398a6f906107bb9083908590615eab565b60405180910390a15050565b6000546001600160a01b031633146107f15760405162461bcd60e51b81526004016104a0906159ba565b6005546001600160a01b03161561081a5760405162461bcd60e51b81526004016104a0906157f0565b61082381611fa5565b61083f5760405162461bcd60e51b81526004016104a090614ee0565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b60006108a684848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6020015190506001600160a01b0381166108d25760405162461bcd60e51b81526004016104a090615574565b6108dd338284611fab565b50505050565b6000546001600160a01b0316331461090d5760405162461bcd60e51b81526004016104a0906159ba565b6109598484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff6121d016565b836001600160a01b03167fcf66acda7673c71cd9458c59067eb30a473eeaf2fa05d008acf47616cbbbe01c84848460405161053293929190614d25565b3360009081526009602052604090205460ff166109c55760405162461bcd60e51b81526004016104a090615bdc565b33600081815260096020526040808220805460ff19168155600101829055517fb771d4b2a83beca38f442c8903629e0e8ab1a07cf76e94eb2977153167e209369190a2565b600b546001600160a01b03163314610a345760405162461bcd60e51b81526004016104a090614df9565b610a41836040015161237a565b15610a5e5760405162461bcd60e51b81526004016104a090615cf0565b610a6b826040015161237a565b15610a885760405162461bcd60e51b81526004016104a0906150fd565b81604001516001600160a01b031683604001516001600160a01b03161415610ac25760405162461bcd60e51b81526004016104a090615455565b610acd8383836123ab565b610ad8838383612620565b610ae28383612731565b600080610af08585856127c3565b91509150610afd836127ee565b610b0a85838684876129e8565b610b15858585612a05565b8260200151604051610b279190614ac2565b60405190819003812084519091610b3e9190614ac2565b60405180910390207fbd75b2e24c3d43ec9f10f3583265606c0a7d8b67d147f9dc48376b81456823a1876040015187604001518760000151886020015189608001518a60a001518b61018001518b8b604051610ba299989796959493929190614c7b565b60405180910390a35050505050565b600b546001600160a01b03163314610bdb5760405162461bcd60e51b81526004016104a090614df9565b610be8816040015161237a565b15610c055760405162461bcd60e51b81526004016104a090615d6b565b6107d06001600160401b0316610c238260c001518360a00151612cff565b6001600160401b03161115610c4a5760405162461bcd60e51b81526004016104a0906156cf565b6000610c5582612d36565b60008181526004602052604090205490915060ff1615610c875760405162461bcd60e51b81526004016104a090615827565b610c8f614140565b600083516001811115610c9e57fe5b14610cbf576080830151610cba9060019063ffffffff611b6216565b610cd4565b610cd483606001516106d58560200151612d74565b90506000610cfc8460c001518560a001516001600160401b0316612e0190919063ffffffff16565b90506000610d0e828460600151611cbb565b60a08601516040808801516001600160a01b03908116600090815260066020908152838220898201519093168252919091529081205492935091610d63916001600160401b039091169063ffffffff612e0116565b90506000610d75828660600151611cbb565b6040888101516001600160a01b039081166000908152600660208181528483208b820180518616855290825285842080546001600160401b0319166001600160401b038b81169190911790915560c08f0151600c548716865293835286852091519095168452905292902054929350610def921690612e43565b600c546001600160a01b0390811660009081526006602090815260408083208a830180518616855292529182902080546001600160401b0319166001600160401b0395909516949094179093556005548a82015193519151636ce5768960e11b815292169263d9caed1292610e6992908890600401614c24565b600060405180830381600087803b158015610e8357600080fd5b505af1158015610e97573d6000803e3d6000fd5b505050600087815260046020908152604091829020805460ff19166001179055870151898201518883015160a08c015193516001600160a01b03938416955091909216927f6960a1f64ecf9da0d1d1bcbfa3dd27f8c1c60de69b13faa28127dafa36c111e492610f0d9290919088908890614d5f565b60405180910390a350505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610f665760405162461bcd60e51b81526004016104a090615779565b6001600160a01b038116610f8c5760405162461bcd60e51b81526004016104a090615c07565b6000546001600160a01b0382811691161415610fba5760405162461bcd60e51b81526004016104a09061502e565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146110065760405162461bcd60e51b81526004016104a0906159ba565b6110518383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392505063ffffffff612e74169050565b826001600160a01b03167f7f63a63f4a2ea318da0b031794d0b5a3183a1b2de54e053ceb17cabdba031735838360405161108c929190614d11565b60405180910390a2505050565b6110a1614140565b6110eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019392508691505063ffffffff611d3b16565b949350505050565b6000546001600160a01b0316331461111d5760405162461bcd60e51b81526004016104a0906159ba565b6001600160a01b0381166111435760405162461bcd60e51b81526004016104a090615c07565b600c546001600160a01b03828116911614156111715760405162461bcd60e51b81526004016104a0906157a7565b600c80546001600160a01b038381166001600160a01b03198316179092556040519116907f9f4f5dce3c4d197b5d7496cb96e25f0a89809167195964b0daa3ef5fed63c00a906107bb9083908590614c61565b6111cd3361237a565b6111e95760405162461bcd60e51b81526004016104a0906153f3565b6111f1614140565b61120260018363ffffffff611b6216565b3360009081526006602090815260408083206001600160a01b038716845290915281205460608301519293506001600160401b031691611243908390611cbb565b9050600081116112655760405162461bcd60e51b81526004016104a090615d92565b3360008181526006602090815260408083206001600160a01b03898116855292529182902080546001600160401b03191690556005549151636ce5768960e11b815291169163d9caed12916112c1919088908690600401614c24565b600060405180830381600087803b1580156112db57600080fd5b505af11580156112ef573d6000803e3d6000fd5b50505050836001600160a01b0316336001600160a01b03167f02e03f232c88c87047c5a9cfbad1213b842503fee6a002ec2eec9f5a64725bf48560400151856000806040516113419493929190614d5f565b60405180910390a350505050565b61135b33600034611fab565b565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113a55760405162461bcd60e51b81526004016104a090615779565b600080546001600160a01b0319169055565b6000818152600860205260409020546001600160401b03165b919050565b6000546001600160a01b031633146113ff5760405162461bcd60e51b81526004016104a0906159ba565b6001600160a01b0381166114255760405162461bcd60e51b81526004016104a090615c07565b600b546001600160a01b03828116911614156114535760405162461bcd60e51b81526004016104a090615888565b600b80546001600160a01b038381166001600160a01b03198316179092556040519116907fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39906107bb9083908590614c61565b60006114b182612d74565b90506114bb613089565b6001600160401b0316816001600160401b0316106114eb5760405162461bcd60e51b81526004016104a0906153b2565b3360009081526007602052604090205460ff161561157457336000908152600760205260409020546001600160401b0380831661010090920416106115425760405162461bcd60e51b81526004016104a090614e9d565b336000908152600760205260409020600101544310156115745760405162461bcd60e51b81526004016104a090615adf565b600a546040805160608101825260018082526001600160401b0385811660208085019182524390960184860181815233600081815260079099529787902095518654935160ff199094169015151768ffffffffffffffff001916610100939094169290920292909217845551929091019190915590519091907f10cf19671b43c88b1f02d4e94932d7ffaa89c7278bc5b8868fa7b7676210809b9061108c90869086908690615e78565b60006001600160a01b0383166116465760405162461bcd60e51b81526004016104a090615c07565b506001600160a01b0391821660009081526006602090815260408083209390941682529190915220546001600160401b031690565b6001600160a01b0382166116a15760405162461bcd60e51b81526004016104a090615738565b6116ac338383611fab565b5050565b3360009081526009602052604090205460ff16156116e05760405162461bcd60e51b81526004016104a090615503565b6040805180820182526001808252600a80544390810160208086019182523360008181526009909252908790209551865460ff19169015151786559051949093019390935554925190927fd60f9f7b2f1a208268475a927bd727c4e198fc8b40aab3004ebcc2bc78ca84809261175892910190615ea2565b60405180910390a2565b60006001600160a01b03841661178a5760405162461bcd60e51b81526004016104a090615c07565b60006117cf84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6020908101516001600160a01b03808816600090815260068452604080822092909316815292529020546001600160401b03169150509392505050565b611814614140565b6001600160a01b0384811660009081526020878152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f81018590048502870185018652808752919592948601939091908301828280156118d75780601f106118ac576101008083540402835291602001916118d7565b820191906000526020600020905b8154815290600101906020018083116118ba57829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091506119335760405162461bcd60e51b81526004016104a09061522b565b8060800151156119555760405162461bcd60e51b81526004016104a090615857565b611963816040015184613096565b61197f5760405162461bcd60e51b81526004016104a090614f09565b8160ff16816060015160ff16146119a85760405162461bcd60e51b81526004016104a090615426565b6001608082018190526001600160401b036103e842021660a08301526001600160a01b0380861660009081526020888152604091829020855181548388015190951661010002610100600160a81b031991151560ff1990961695909517169390931783559084015180518594611a2393908501920190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790556040516001860190611a8d908590614ac2565b9081526040805191829003602090810190922080546001818101835560009283529184902085516003909202018054868601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559184015180518594611b04938501920190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790555050505050565b611b6a614140565b6001600160a01b038216611b8757611b806130ef565b905061065c565b611b8f614140565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015611c525780601f10611c2757610100808354040283529160200191611c52565b820191906000526020600020905b815481529060010190602001808311611c3557829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015611c9f575080608001515b61072f5760405162461bcd60e51b81526004016104a090615976565b600060208260ff161115611ce15760405162461bcd60e51b81526004016104a090615a15565b60088260ff161115611d1057611b806001600160401b03841660ff600719850116600a0a63ffffffff61314816565b61072f6001600160401b03841660ff600885900316600a0a63ffffffff61318216565b426103e80290565b611d43614140565b611d686040518060400160405280600381526020016208aa8960eb1b81525084613096565b15611d7c57611d756130ef565b905061072f565b611d84614140565b60008560010185604051611d989190614ac2565b908152604051908190036020019020541115611f7a5760005b8560010185604051611dc39190614ac2565b9081526040519081900360200190205460ff82161015611f7857836001600160401b03168660010186604051611df99190614ac2565b90815260200160405180910390208260ff1681548110611e1557fe5b60009182526020909120600390910201600201546201000090046001600160401b031611611f70578560010185604051611e4f9190614ac2565b90815260200160405180910390208160ff1681548110611e6b57fe5b60009182526020918290206040805160c081018252600393909302909101805460ff811615158452610100908190046001600160a01b031684860152600180830180548551600293821615909402600019011691909104601f8101879004870283018701855280835294959294938601939192909190830182828015611f325780601f10611f0757610100808354040283529160200191611f32565b820191906000526020600020905b815481529060010190602001808311611f1557829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015291505b600101611db1565b505b80518015611f89575080608001515b6110eb5760405162461bcd60e51b81526004016104a090615621565b3b151590565b6001600160a01b03831660009081526009602052604090205460ff1615611fe45760405162461bcd60e51b81526004016104a090615d6b565b611fec614140565b611ffd60018463ffffffff611b6216565b9050600061200f8383606001516131c1565b90506000816001600160401b03161161203a5760405162461bcd60e51b81526004016104a090615168565b600061204a828460600151611cbb565b90506001600160a01b0385161561206657612066868683613256565b60055461207d906001600160a01b031686836133e9565b6001600160a01b0380871660009081526006602090815260408083209389168352929052908120546120be906001600160401b03168463ffffffff612e4316565b905060006120d0828660600151611cbb565b6001600160a01b038981166000908152600660209081526040808320938c16835292905281902080546001600160401b0319166001600160401b03868116919091179091556005805467ffffffffffffffff60a01b198116600160a01b9182900484166001019093160291909117905586810151905191925061215291614ac2565b6040518091039020876001600160a01b0316896001600160a01b03167fc2813a84168de59cf710030ba2acb13031b567b8ba76f70e8ab0782aa9469a7a600560149054906101000a90046001600160401b031689604001518988886040516121be959493929190615ecd565b60405180910390a45050505050505050565b60208160ff1611156121f45760405162461bcd60e51b81526004016104a09061536b565b6001600160a01b03831615801590612210575061221083611fa5565b61222c5760405162461bcd60e51b81526004016104a090615ab0565b600082511161224d5760405162461bcd60e51b81526004016104a090615bae565b6001600160a01b038316600090815260208590526040902060020154610100900460ff161561228e5760405162461bcd60e51b81526004016104a090615857565b6040805160c08101825260018082526001600160a01b03868116602080850182815285870189815260ff8916606088015260006080880181905260a088018190529384528b83529690922085518154935160ff1990941690151517610100600160a81b03191661010093909416929092029290921781559351805193949361231d938501929190910190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff199093169290921793909316179290921617905550505050565b6001600160a01b0381166000908152600960205260408120805460ff16801561072f57506001015443101592915050565b80606001516001600160a01b031681604001516001600160a01b031614156123e55760405162461bcd60e51b81526004016104a090615487565b6123ed614140565b61240282600001516106d58660200151612d74565b905061240c614140565b61242183602001516106d58760200151612d74565b905082604001516001600160a01b031682602001516001600160a01b0316148015612465575082606001516001600160a01b031681602001516001600160a01b0316145b6124815760405162461bcd60e51b81526004016104a090615195565b612489614140565b61249e84600001516106d58760200151612d74565b90506124a8614140565b6124bd85602001516106d58860200151612d74565b905084604001516001600160a01b031682602001516001600160a01b0316148015612501575084606001516001600160a01b031681602001516001600160a01b0316145b61251d5760405162461bcd60e51b81526004016104a090615a5c565b84604001516001600160a01b03168561010001516001600160a01b03161480612560575084606001516001600160a01b03168561010001516001600160a01b0316145b61257c5760405162461bcd60e51b81526004016104a090615327565b84604001516001600160a01b03168561012001516001600160a01b031614806125bf575084606001516001600160a01b03168561012001516001600160a01b0316145b6125db5760405162461bcd60e51b81526004016104a0906155dd565b8461012001516001600160a01b03168561010001516001600160a01b031614156126175760405162461bcd60e51b81526004016104a090614fe2565b50505050505050565b600081608001516001600160401b03161161264d5760405162461bcd60e51b81526004016104a090615dc0565b60008160a001516001600160401b03161161267a5760405162461bcd60e51b81526004016104a090615d23565b61268783606001516135ce565b156126d3578060a001516001600160401b03166126ac82608001518560e00151613627565b6001600160401b031610156126d35760405162461bcd60e51b81526004016104a0906158d1565b6126e082606001516135ce565b1561272c578060a001516001600160401b031661270582608001518460e00151613627565b6001600160401b0316111561272c5760405162461bcd60e51b81526004016104a090615b77565b505050565b61273e8260400151613680565b6001600160401b03166127548360200151612d74565b6001600160401b03161161277a5760405162461bcd60e51b81526004016104a090615935565b6127878160400151613680565b6001600160401b031661279d8260200151612d74565b6001600160401b0316116116ac5760405162461bcd60e51b81526004016104a090615252565b60008060006127d286856136fd565b905060006127e086866136fd565b919791965090945050505050565b600081604001516001600160a01b03168261010001516001600160a01b03161461281c578160a00151612822565b81608001515b90506107d06001600160401b031661283f83610140015183612cff565b6001600160401b031611156128665760405162461bcd60e51b81526004016104a09061509b565b600082604001516001600160a01b03168361012001516001600160a01b031614612894578260a0015161289a565b82608001515b90506107d06001600160401b03166128b784610160015183612cff565b6001600160401b031611156128de5760405162461bcd60e51b81526004016104a090614f6e565b82608001516001600160401b031661293d84604001516001600160a01b03168561010001516001600160a01b03161461291c57846101600151612923565b8461014001515b60c08601516001600160401b03169063ffffffff612e4316565b6001600160401b0316146129635760405162461bcd60e51b81526004016104a090614f9b565b8260a001516001600160401b03166129c284606001516001600160a01b03168561010001516001600160a01b0316146129a1578461016001516129a8565b8461014001515b60e08601516001600160401b03169063ffffffff612e4316565b6001600160401b03161461272c5760405162461bcd60e51b81526004016104a090615b2f565b6129f385858361379e565b6129fe83838361379e565b5050505050565b60808101516040808401516001600160a01b03908116600090815260066020908152838220848701519093168252919091522054612a54916001600160401b039091169063ffffffff612e0116565b6040838101516001600160a01b0390811660009081526006602081815284832087860180518616855290825285842080546001600160401b0319166001600160401b0398891617905560c08801518a8701518616855292825285842090519094168352929092529190912054612acb921690612e43565b604084810180516001600160a01b039081166000908152600660208181528583208887015185168452815285832080546001600160401b0319166001600160401b0398891617905560a088015194518416835290815284822060608801519093168252919091529190912054612b42921690612e01565b6040848101516001600160a01b039081166000908152600660208181528483206060880180518616855290825285842080546001600160401b0319166001600160401b0398891617905560e0880151898701518616855292825285842090519094168352929092529190912054612bba921690612e43565b6040838101516001600160a01b03908116600090815260066020818152848320606088015185168452815284832080546001600160401b0319166001600160401b03978816179055610140870151600c54851684529181528483206101008801519094168352929092529190912054612c34921690612e43565b600c80546001600160a01b03908116600090815260066020818152604080842061010089015186168552825280842080546001600160401b0319166001600160401b039889161790556101608801519554851684529181528183206101208801519094168352929092522054612cab921690612e43565b600c546001600160a01b039081166000908152600660209081526040808320610120969096015190931682529390935290912080546001600160401b0319166001600160401b039092169190911790555050565b600061271061065883612d216001600160401b0387168463ffffffff61392216565b6001600160401b03169063ffffffff61397d16565b600080612d42836139bc565b9050612d58818461010001518560400151613a77565b61065c5760405162461bcd60e51b81526004016104a090614dc7565b6000600f604c83901c1660018114612d9e5760405162461bcd60e51b81526004016104a090615072565b610fff60301b601084901c1665ffff00000000603085901c1663ffffffff606086901c168183178117612df6650b1d069b54006127106001600160801b038416046001600160401b0316612e0190919063ffffffff16565b979650505050505050565b600061072f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613aa7565b60008282016001600160401b03808516908216101561072f5760405162461bcd60e51b81526004016104a0906150c8565b612e7c614140565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015612f3f5780601f10612f1457610100808354040283529160200191612f3f565b820191906000526020600020905b815481529060010190602001808311612f2257829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015612f8c575080608001515b612fa85760405162461bcd60e51b81526004016104a0906151e8565b612fcd826040518060400160405280600381526020016208aa8960eb1b815250613096565b15612fea5760405162461bcd60e51b81526004016104a090615131565b426103e89081026001600160401b031660a08301526040516001860190613012908590614ac2565b9081526040805191829003602090810190922080546001818101835560009283529184902086516003909202018054878601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559185015180518694611b04938501920190614176565b4262015180016103e80290565b6000816040516020016130a99190614ac2565b60405160208183030381529060405280519060200120836040516020016130d09190614ac2565b6040516020818303038152906040528051906020012014905092915050565b6130f7614140565b506040805160c08101825260018082526000602080840182905284518086018652600381526208aa8960eb1b918101919091529383019390935260126060830152608082015260a081019190915290565b6000826131575750600061065c565b8282028284828161316457fe5b041461072f5760405162461bcd60e51b81526004016104a09061568e565b600061072f83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250613ae6565b600060208260ff1611156131e75760405162461bcd60e51b81526004016104a090615a15565b600060088360ff1611156132165761320f8460ff600719860116600a0a63ffffffff61318216565b9050613233565b6132308460ff600886900316600a0a63ffffffff61314816565b90505b600160401b811061072f5760405162461bcd60e51b81526004016104a090615701565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190613285903090600401614c10565b60206040518083038186803b15801561329d57600080fd5b505afa1580156132b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132d5919061497d565b6040516323b872dd60e01b81529091506001600160a01b038416906323b872dd9061330890879030908790600401614c24565b600060405180830381600087803b15801561332257600080fd5b505af1158015613336573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a0823190613369903090600401614c10565b60206040518083038186803b15801561338157600080fd5b505afa158015613395573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b9919061497d565b9050826133cc828463ffffffff613b1d16565b146129fe5760405162461bcd60e51b81526004016104a090614e2b565b6001600160a01b03821661343d576040516001600160a01b0384169082156108fc029083906000818181858888f193505050506134385760405162461bcd60e51b81526004016104a0906159e8565b61272c565b6040516370a0823160e01b81526000906001600160a01b038416906370a082319061346c908790600401614c10565b60206040518083038186803b15801561348457600080fd5b505afa158015613498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134bc919061497d565b60405163a9059cbb60e01b81529091506001600160a01b0384169063a9059cbb906134ed9087908690600401614c48565b600060405180830381600087803b15801561350757600080fd5b505af115801561351b573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a082319061354e908890600401614c10565b60206040518083038186803b15801561356657600080fd5b505afa15801561357a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359e919061497d565b9050826135b1828463ffffffff613b1d16565b146129fe5760405162461bcd60e51b81526004016104a090615c82565b600060018260068111156135de57fe5b14806135f5575060028260068111156135f357fe5b145b8061360b5750600482600681111561360957fe5b145b8061065c575060065b82600681111561362057fe5b1492915050565b60006305f5e1008161365b8261364f6001600160401b0388811690881663ffffffff61314816565b9063ffffffff61318216565b9050600160401b81106106585760405162461bcd60e51b81526004016104a090615c37565b6001600160a01b03811660009081526007602052604081205460ff1680156136c357506001600160a01b0382166000908152600760205260409020600101544310155b156136f557506001600160a01b03811660009081526007602052604090205461010090046001600160401b03166113d0565b506000919050565b6000806137138484600001518560200151613b5f565b905061372981856101a001518660400151613a77565b60008560800151600181111561373b57fe5b1461375e57604051806060016040528060278152602001615f9d60279139613778565b604051806060016040528060268152602001615f77602691395b906137965760405162461bcd60e51b81526004016104a09190614d4c565b509392505050565b60008281526003602052604090205460ff16156137cd5760405162461bcd60e51b81526004016104a090615908565b60008360c0015115613839576137e68460600151613d01565b6138025760405162461bcd60e51b81526004016104a090615294565b60008381526008602052604090205460a0830151613832916001600160401b03918216911663ffffffff612e4316565b905061386c565b6000838152600860205260409020546080830151613869916001600160401b03918216911663ffffffff612e4316565b90505b8360a001516001600160401b0316816001600160401b031611156138a25760405162461bcd60e51b81526004016104a090615664565b8360a001516001600160401b0316816001600160401b031610156138ec57600083815260086020526040902080546001600160401b0319166001600160401b0383161790556108dd565b5050600090815260086020908152604080832080546001600160401b031916905560039091529020805460ff1916600117905550565b60006001600160401b03831661393a5750600061065c565b8282026001600160401b03808416908086169083168161395657fe5b046001600160401b03161461072f5760405162461bcd60e51b81526004016104a09061568e565b600061072f83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250613d34565b602081015160408201516000919082845160018111156139d857fe5b14613a065783608001516040516020016139f291906149eb565b604051602081830303815290604052613a2b565b8360600151604051602001613a1b9190614ac2565b6040516020818303038152906040525b613a418560a001516001600160401b0316613d7c565b8560e00151604051602001613a5a959493929190614b0f565b604051602081830303815290604052805190602001209050919050565b6000816001600160a01b0316613a95613a8f86613e78565b85613e8b565b6001600160a01b031614949350505050565b6000836001600160401b0316836001600160401b031611158290613ade5760405162461bcd60e51b81526004016104a09190614d4c565b505050900390565b60008183613b075760405162461bcd60e51b81526004016104a09190614d4c565b506000838581613b1357fe5b0495945050505050565b600061072f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613faf565b6000836000015160ff16600114613b885760405162461bcd60e51b81526004016104a0906154ce565b835160208501516040860151613b9e8686613fd3565b87606001516006811115613bae57fe5b88608001516001811115613bbe57fe5b613bd48a60a001516001600160401b0316613d7c565b604051602001613bea9796959493929190614b7d565b6040516020818303038152906040528460c0015160008660e001516001600160401b031611613c285760405180602001604052806000815250613c3e565b613c3e8660e001516001600160401b0316613d7c565b60008761010001516001600160401b031611613c695760405180602001604052806000815250613c80565b613c808761010001516001600160401b0316613d7c565b8761012001518861014001516003811115613c9757fe5b8961016001516003811115613ca857fe5b8a6101800151604051602001613cc49796959493929190614a03565b60408051601f1981840301815290829052613ce29291602001614a94565b6040516020818303038152906040528051906020012090509392505050565b600080826006811115613d1057fe5b1480613d2757506003826006811115613d2557fe5b145b8061065c57506005613614565b6000816001600160401b038416613d5e5760405162461bcd60e51b81526004016104a09190614d4c565b506000836001600160401b0316856001600160401b031681613b1357fe5b60608160005b8115613d9657600101600a82049150613d82565b6009811015613da3575060095b6001016060816001600160401b0381118015613dbe57600080fd5b506040519080825280601f01601f191660200182016040528015613de9576020820181803683370190505b509050815b8015613e6f5780830360081415613e2f57602e60f81b826001830381518110613e1357fe5b60200101906001600160f81b031916908160001a905350613e66565b600a860660300160f81b826001830381518110613e4857fe5b60200101906001600160f81b031916908160001a905350600a860495505b60001901613dee565b50949350505050565b600081604051602001613a5a9190614ade565b60008151604114613eae5760405162461bcd60e51b81526004016104a090614f37565b60208201516040830151606084015160001a6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03821115613ef65760405162461bcd60e51b81526004016104a0906152e5565b8060ff16601b14158015613f0e57508060ff16601c14155b15613f2b5760405162461bcd60e51b81526004016104a090615532565b600060018783868660405160008152602001604052604051613f509493929190614cf3565b6020604051602081039080840390855afa158015613f72573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613fa55760405162461bcd60e51b81526004016104a090614d95565b9695505050505050565b60008184841115613ade5760405162461bcd60e51b81526004016104a09190614d4c565b604080518082019091526001808252602d60f81b60208301528251845160609386939092869286929101016001600160401b038111801561401357600080fd5b506040519080825280601f01601f19166020018201604052801561403e576020820181803683370190505b5090506000805b85518210156140995785828151811061405a57fe5b602001015160f81c60f81b83828060010193508151811061407757fe5b60200101906001600160f81b031916908160001a905350600190910190614045565b846000815181106140a657fe5b602001015160f81c60f81b8382806001019350815181106140c357fe5b60200101906001600160f81b031916908160001a905350600091505b8351821015614133578382815181106140f457fe5b602001015160f81c60f81b83828060010193508151811061411157fe5b60200101906001600160f81b031916908160001a9053506001909101906140df565b5090979650505050505050565b6040805160c08101825260008082526020820181905260609282018390529181018290526080810182905260a081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106141b757805160ff19168380011785556141e4565b828001600101855582156141e4579182015b828111156141e45782518255916020019190600101906141c9565b506141f09291506141f4565b5090565b61054d91905b808211156141f057600081556001016141fa565b803561065c81615f5e565b8035801515811461065c57600080fd5b600082601f830112614239578081fd5b81356001600160401b0381111561424e578182fd5b614261601f8201601f1916602001615f0c565b915080825283602082850101111561427857600080fd5b8060208401602084013760009082016020015292915050565b80356004811061065c57600080fd5b80356002811061065c57600080fd5b80356007811061065c57600080fd5b60008083601f8401126142cf578182fd5b5081356001600160401b038111156142e5578182fd5b6020830191508360208285010111156142fd57600080fd5b9250929050565b60006101c0808385031215614317578182fd5b61432081615f0c565b91505061432d838361448a565b815261433c836020840161445c565b602082015261434e836040840161420e565b604082015261436083606084016142af565b606082015261437283608084016142a0565b60808201526143848360a08401614473565b60a08201526143968360c08401614219565b60c08201526143a88360e08401614473565b60e08201526101006143bc84828501614473565b90820152610120828101356001600160401b03808211156143dc57600080fd5b6143e886838701614229565b8385015261014092506143fd86848701614291565b83850152610160925061441286848701614291565b83850152610180925061442786848701614473565b838501526101a092508285013591508082111561444357600080fd5b5061445085828601614229565b82840152505092915050565b80356001600160801b038116811461065c57600080fd5b80356001600160401b038116811461065c57600080fd5b803560ff8116811461065c57600080fd5b6000602082840312156144ac578081fd5b813561072f81615f5e565b600080604083850312156144c9578081fd5b82356144d481615f5e565b915060208301356144e481615f5e565b809150509250929050565b600080600060408486031215614503578081fd5b833561450e81615f5e565b925060208401356001600160401b03811115614528578182fd5b614534868287016142be565b9497909650939450505050565b600060208284031215614552578081fd5b5035919050565b6000806000806060858703121561456e578182fd5b843561457981615f5e565b935060208501356001600160401b03811115614593578283fd5b61459f878288016142be565b90945092506145b39050866040870161448a565b905092959194509250565b600080604083850312156145d0578182fd5b82356145db81615f5e565b946020939093013593505050565b6000806000604084860312156145fd578081fd5b83356001600160401b03811115614612578182fd5b61461e868287016142be565b909790965060209590950135949350505050565b600080600060408486031215614646578081fd5b83356001600160401b038082111561465c578283fd5b614668878388016142be565b9095509350602086013591508082168214614681578283fd5b50809150509250925092565b6000806000606084860312156146a1578081fd5b83356001600160401b03808211156146b7578283fd5b6146c387838801614304565b945060208601359150808211156146d8578283fd5b6146e487838801614304565b935060408601359150808211156146f9578283fd5b6101c091860180880383131561470d578384fd5b61471683615f0c565b8135935082841115614726578485fd5b61473289858401614229565b81526020820135935082841115614747578485fd5b61475389858401614229565b6020820152614765896040840161420e565b6040820152614777896060840161420e565b60608201526147898960808401614473565b608082015261479b8960a08401614473565b60a08201526147ad8960c08401614473565b60c08201526147bf8960e08401614473565b60e082015261010093506147d58985840161420e565b8482015261012093506147ea8985840161420e565b8482015261014093506147ff89858401614473565b84820152610160935061481489858401614473565b84820152610180935061482989858401614473565b848201526101a0935061483e898584016142a0565b84820152809450505050509250925092565b600060208284031215614861578081fd5b81356001600160401b0380821115614877578283fd5b61012091840180860383131561488b578384fd5b61489483615f0c565b61489e87836142a0565b81526148ad876020840161445c565b60208201526148bf876040840161420e565b604082015260608201359350828411156148d7578485fd5b6148e387858401614229565b60608201526148f5876080840161420e565b60808201526149078760a08401614473565b60a08201526149198760c08401614473565b60c082015261492b8760e08401614219565b60e082015261010093508382013583811115614945578586fd5b61495188828501614229565b948201949094529695505050505050565b600060208284031215614973578081fd5b61072f838361445c565b60006020828403121561498e578081fd5b5051919050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600081518084526149d7816020860160208601615f32565b601f01601f19169290920160200192915050565b60609190911b6001600160601b031916815260140190565b600088151560f81b82528751614a20816001850160208c01615f32565b8751908301614a36826001830160208c01615f32565b8751918101614a4c836001830160208c01615f32565b60f897881b6001600160f81b0319908116600192909401918201939093529590961b166002850152505060c01b6001600160c01b0319166003820152600b0195945050505050565b60008351614aa6818460208801615f32565b8351908301614ab9828260208801615f32565b01949350505050565b60008251614ad4818460208701615f32565b9190910192915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b608086901b6001600160801b0319168152606085901b6001600160601b03191660108201528351600090614b4a816024850160208901615f32565b8451908301614b60826024830160208901615f32565b93151560f81b602494909101938401525050602501949350505050565b6001600160f81b031960f889901b811682526001600160801b0319608089901b1660018301526001600160601b0319606088901b166011830152855160009190614bce816025860160208b01615f32565b808401828860f81b166025820152828760f81b16602682015285519250614bfc836027830160208901615f32565b9091016027019a9950505050505050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038a811682528916602082015261012060408201819052600090614ca88382018b6149bf565b8381036060850152614cba818b6149bf565b6001600160401b03998a16608086015297891660a08501525050509290941660c083015260e08201526101000191909152949350505050565b93845260ff9290921660208401526040830152606082015260800190565b6000602082526110eb602083018486614995565b600060408252614d39604083018587614995565b905060ff83166020830152949350505050565b60006020825261072f60208301846149bf565b600060808252614d7260808301876149bf565b6001600160401b0395861660208401529390941660408201526060015292915050565b60208082526018908201527745434453413a20696e76616c6964207369676e617475726560401b604082015260600190565b602080825260189082015277496e76616c69642077616c6c6574207369676e617475726560401b604082015260600190565b60208082526018908201527721b0b63632b91034b9903737ba103234b9b830ba31b432b960411b604082015260600190565b6020808252604c908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f46726f6d207375636365737320776974686f757420657870656374656420626160608201526b6c616e6365206368616e676560a01b608082015260a00190565b60208082526023908201527f4e6f6e63652074696d657374616d7020616c726561647920696e76616c6964616040820152621d195960ea1b606082015260800190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252601490820152730a6f2dac4ded8e640c8de40dcdee840dac2e8c6d60631b604082015260600190565b6020808252601f908201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604082015260600190565b6020808252601390820152724578636573736976652074616b65722066656560681b604082015260600190565b60208082526027908201527f4e6574206261736520706c757320666565206973206e6f7420657175616c20746040820152666f2067726f737360c81b606082015260800190565b6020808252602c908201527f4d616b657220616e642074616b65722066656520617373657473206d7573742060408201526b189948191a5999995c995b9d60a21b606082015260800190565b60208082526024908201527f4d75737420626520646966666572656e742066726f6d2063757272656e7420616040820152633236b4b760e11b606082015260800190565b6020808252600f908201526e135d5cdd081899481d8c4815555251608a1b604082015260600190565b602080825260139082015272457863657373697665206d616b65722066656560681b604082015260600190565b6020808252601b908201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604082015260600190565b6020808252601a908201527914d95b1b081dd85b1b195d08195e1a5d08199a5b985b1a5e995960321b604082015260600190565b6020808252601d908201527f4554482073796d626f6c20726573657276656420666f72204574686572000000604082015260600190565b6020808252601390820152725175616e7469747920697320746f6f206c6f7760681b604082015260600190565b60208082526033908201527f427579206f72646572206d61726b65742073796d626f6c2061646472657373206040820152720e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d606b1b606082015260800190565b60208082526023908201527f526567697374726174696f6e206f6620746f6b656e206e6f742066696e616c696040820152621e995960ea1b606082015260800190565b6020808252600d908201526c2ab735b737bbb7103a37b5b2b760991b604082015260600190565b60208082526022908201527f53656c6c206f72646572206e6f6e63652074696d657374616d7020746f6f206c6040820152616f7760f01b606082015260800190565b60208082526031908201527f4f726465722071756f7465207175616e74697479206f6e6c792076616c696420604082015270666f72206d61726b6574206f726465727360781b606082015260800190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604082015261756560f01b606082015260800190565b60208082526024908201527f4d616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526027908201527f546f6b656e2063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b60208082526021908201527f4e6f6e63652074696d657374616d7020746f6f2066617220696e2066757475726040820152606560f81b606082015260800190565b60208082526019908201527815d85b1b195d08195e1a5d081b9bdd08199a5b985b1a5e9959603a1b604082015260600190565b602080825260159082015274088cac6d2dac2d8e640c8de40dcdee840dac2e8c6d605b1b604082015260600190565b60208082526018908201527714d95b198b5d1c98591a5b99c81b9bdd08185b1b1bddd95960421b604082015260600190565b60208082526027908201527f4261736520616e642071756f746520617373657473206d7573742062652064696040820152661999995c995b9d60ca1b606082015260800190565b6020808252818101527f5369676e617475726520686173682076657273696f6e206d7573742062652031604082015260600190565b60208082526015908201527415d85b1b195d08185b1c9958591e48195e1a5d1959605a1b604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604082015261756560f01b606082015260800190565b6020808252601f908201527f557365206465706f736974457468657220746f206465706f7369742045544800604082015260600190565b6020808252601890820152774d757374206265206c657373207468616e2031207765656b60401b604082015260600190565b60208082526024908201527f54616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526023908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f722073796d604082015262189bdb60ea1b606082015260800190565b60208082526010908201526f13dc99195c881bdd995c999a5b1b195960821b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b602080825260189082015277457863657373697665207769746864726177616c2066656560401b604082015260600190565b6020808252601d908201527f506970207175616e74697479206f766572666c6f77732075696e743634000000604082015260600190565b60208082526021908201527f557365206465706f736974457468657220746f206465706f73697420457468656040820152603960f91b606082015260800190565b60208082526014908201527321b0b63632b91036bab9ba1031329037bbb732b960611b604082015260600190565b60208082526029908201527f4d75737420626520646966666572656e742066726f6d2063757272656e7420666040820152681959481dd85b1b195d60ba1b606082015260800190565b6020808252601e908201527f437573746f6469616e2063616e206f6e6c7920626520736574206f6e63650000604082015260600190565b6020808252601690820152752430b9b41030b63932b0b23c903bb4ba34323930bbb760511b604082015260600190565b602080825260179082015276151bdad95b88185b1c9958591e48199a5b985b1a5e9959604a1b604082015260600190565b60208082526029908201527f4d75737420626520646966666572656e742066726f6d2063757272656e74206460408201526834b9b830ba31b432b960b91b606082015260800190565b6020808252601e908201527f427579206f72646572206c696d69742070726963652065786365656465640000604082015260600190565b60208082526013908201527213dc99195c88191bdd589b1948199a5b1b1959606a1b604082015260600190565b60208082526021908201527f427579206f72646572206e6f6e63652074696d657374616d7020746f6f206c6f6040820152607760f81b606082015260800190565b60208082526024908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f72206164646040820152637265737360e01b606082015260800190565b60208082526014908201527321b0b63632b91036bab9ba1031329030b236b4b760611b604082015260600190565b602080825260139082015272115512081d1c985b9cd9995c8819985a5b1959606a1b604082015260600190565b60208082526027908201527f41737365742063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b60208082526034908201527f53656c6c206f72646572206d61726b65742073796d626f6c2061646472657373604082015273040e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d60631b606082015260800190565b602080825260159082015274496e76616c696420746f6b656e206164647265737360581b604082015260600190565b60208082526030908201527f50726576696f757320696e76616c69646174696f6e206177616974696e67206360408201526f3430b4b710383937b830b3b0ba34b7b760811b606082015260800190565b60208082526028908201527f4e65742071756f746520706c757320666565206973206e6f7420657175616c20604082015267746f2067726f737360c01b606082015260800190565b6020808252601f908201527f53656c6c206f72646572206c696d697420707269636520657863656564656400604082015260600190565b602080825260149082015273125b9d985b1a59081d1bdad95b881cde5b589bdb60621b604082015260600190565b60208082526011908201527015d85b1b195d081b9bdd08195e1a5d1959607a1b604082015260600190565b602080825260169082015275496e76616c69642077616c6c6574206164647265737360501b604082015260600190565b6020808252602b908201527f496d706c6965642071756f746520706970207175616e74697479206f7665726660408201526a1b1bdddcc81d5a5b9d0d8d60aa1b606082015260800190565b60208082526048908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f207375636365737320776974686f75742065787065637465642062616c616e6360608201526765206368616e676560c01b608082015260a00190565b602080825260199082015278109d5e481dd85b1b195d08195e1a5d08199a5b985b1a5e9959603a1b604082015260600190565b60208082526028908201527f51756f7465207175616e74697479206d7573742062652067726561746572207460408201526768616e207a65726f60c01b606082015260800190565b6020808252600d908201526c15d85b1b195d08195e1a5d1959609a1b604082015260600190565b602080825260149082015273139bc818985b185b98d948199bdc88185cdcd95d60621b604082015260600190565b60208082526027908201527f42617365207175616e74697479206d7573742062652067726561746572207468604082015266616e207a65726f60c81b606082015260800190565b60006020825282511515602083015260018060a01b036020840151166040830152604083015160c06060840152615e4160e08401826149bf565b60ff60608601511660808501526080850151151560a08501526001600160401b0360a08601511660c0850152809250505092915050565b6001600160801b039390931683526001600160401b03919091166020830152604082015260600190565b90815260200190565b918252602082015260400190565b6001600160401b0391909116815260200190565b60006001600160401b03808816835260a06020840152615ef060a08401886149bf565b9581166040840152939093166060820152608001525092915050565b6040518181016001600160401b0381118282101715615f2a57600080fd5b604052919050565b60005b83811015615f4d578181015183820152602001615f35565b838111156108dd5750506000910152565b6001600160a01b0381168114615f7357600080fd5b5056fe496e76616c69642077616c6c6574207369676e617475726520666f7220627579206f72646572496e76616c69642077616c6c6574207369676e617475726520666f722073656c6c206f72646572a26469706673582212205dd7ac35c5f7c6f5ec79f4ba79633e7c75138d994328bbe33fa2efc0d21be22d64736f6c63430006080033

Deployed Bytecode

0x6080604052600436106101415760003560e01c806372e8f08d116100b157806372e8f08d146102ea578063869af2121461030a57806390d49b9d14610337578063985c4af51461035757806398ea5fca146103775780639a202d471461037f578063ae0e969e14610394578063ba22bd76146103c1578063d7a6aec7146103e1578063dbb3653514610401578063dcc6349014610421578063eb5068f214610441578063ef3b9d4a1461045657610141565b80630226b70e1461014657806302ca6002146101685780630c187a721461019357806313cfda2c146101a85780631abb5832146101d557806320e6a8e3146101f5578063403f3731146102155780634196818214610235578063457aa3c6146102555780634a284ef91461027557806365fce5b71461028a5780636bb509f2146102aa578063704b6c02146102ca575b600080fd5b34801561015257600080fd5b50610166610161366004614559565b610476565b005b34801561017457600080fd5b5061017d610540565b60405161018a9190614c10565b60405180910390f35b34801561019f57600080fd5b50610166610550565b3480156101b457600080fd5b506101c86101c33660046144b7565b6105d4565b60405161018a9190615ea2565b3480156101e157600080fd5b506101c86101f03660046144ef565b610662565b34801561020157600080fd5b50610166610210366004614541565b610736565b34801561022157600080fd5b5061016661023036600461449b565b6107c7565b34801561024157600080fd5b506101666102503660046145e9565b610861565b34801561026157600080fd5b50610166610270366004614559565b6108e3565b34801561028157600080fd5b50610166610996565b34801561029657600080fd5b506101666102a536600461468d565b610a0a565b3480156102b657600080fd5b506101666102c5366004614850565b610bb1565b3480156102d657600080fd5b506101666102e536600461449b565b610f1e565b3480156102f657600080fd5b506101666103053660046144ef565b610fdc565b34801561031657600080fd5b5061032a610325366004614632565b611099565b60405161018a9190615e07565b34801561034357600080fd5b5061016661035236600461449b565b6110f3565b34801561036357600080fd5b5061016661037236600461449b565b6111c4565b61016661134f565b34801561038b57600080fd5b5061016661135d565b3480156103a057600080fd5b506103b46103af366004614541565b6113b7565b60405161018a9190615eb9565b3480156103cd57600080fd5b506101666103dc36600461449b565b6113d5565b3480156103ed57600080fd5b506101666103fc366004614962565b6114a6565b34801561040d57600080fd5b506103b461041c3660046144b7565b61161e565b34801561042d57600080fd5b5061016661043c3660046145be565b61167b565b34801561044d57600080fd5b506101666116b0565b34801561046257600080fd5b506103b46104713660046144ef565b611762565b6000546001600160a01b031633146104a95760405162461bcd60e51b81526004016104a0906159ba565b60405180910390fd5b6104f58484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff61180c16565b836001600160a01b03167fa8968236dc4afb2c189afeb69c91b02445e6bd09f0ffe27256b62838edc99edb84848460405161053293929190614d25565b60405180910390a250505050565b600c546001600160a01b03165b90565b6000546001600160a01b0316331461057a5760405162461bcd60e51b81526004016104a0906159ba565b600b546040517fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39916105ba916001600160a01b0390911690600090614c61565b60405180910390a1600b80546001600160a01b0319169055565b60006001600160a01b0383166105fc5760405162461bcd60e51b81526004016104a090615c07565b610604614140565b61061560018463ffffffff611b6216565b6001600160a01b038086166000908152600660209081526040808320938816835292905220546060820151919250610658916001600160401b0390911690611cbb565b9150505b92915050565b60006001600160a01b03841661068a5760405162461bcd60e51b81526004016104a090615c07565b610692614140565b6106e484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6001919063ffffffff611d3b16565b6001600160a01b0380871660009081526006602090815260408083208286015190941683529290522054606082015191925061072b916001600160401b0390911690611cbb565b9150505b9392505050565b6000546001600160a01b031633146107605760405162461bcd60e51b81526004016104a0906159ba565b619d8081106107815760405162461bcd60e51b81526004016104a0906155ab565b600a8054908290556040517f9a22227d6c0251a79ef8b846202ddcbe9d682ee5482e84abeec6dda096398a6f906107bb9083908590615eab565b60405180910390a15050565b6000546001600160a01b031633146107f15760405162461bcd60e51b81526004016104a0906159ba565b6005546001600160a01b03161561081a5760405162461bcd60e51b81526004016104a0906157f0565b61082381611fa5565b61083f5760405162461bcd60e51b81526004016104a090614ee0565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b60006108a684848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6020015190506001600160a01b0381166108d25760405162461bcd60e51b81526004016104a090615574565b6108dd338284611fab565b50505050565b6000546001600160a01b0316331461090d5760405162461bcd60e51b81526004016104a0906159ba565b6109598484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff6121d016565b836001600160a01b03167fcf66acda7673c71cd9458c59067eb30a473eeaf2fa05d008acf47616cbbbe01c84848460405161053293929190614d25565b3360009081526009602052604090205460ff166109c55760405162461bcd60e51b81526004016104a090615bdc565b33600081815260096020526040808220805460ff19168155600101829055517fb771d4b2a83beca38f442c8903629e0e8ab1a07cf76e94eb2977153167e209369190a2565b600b546001600160a01b03163314610a345760405162461bcd60e51b81526004016104a090614df9565b610a41836040015161237a565b15610a5e5760405162461bcd60e51b81526004016104a090615cf0565b610a6b826040015161237a565b15610a885760405162461bcd60e51b81526004016104a0906150fd565b81604001516001600160a01b031683604001516001600160a01b03161415610ac25760405162461bcd60e51b81526004016104a090615455565b610acd8383836123ab565b610ad8838383612620565b610ae28383612731565b600080610af08585856127c3565b91509150610afd836127ee565b610b0a85838684876129e8565b610b15858585612a05565b8260200151604051610b279190614ac2565b60405190819003812084519091610b3e9190614ac2565b60405180910390207fbd75b2e24c3d43ec9f10f3583265606c0a7d8b67d147f9dc48376b81456823a1876040015187604001518760000151886020015189608001518a60a001518b61018001518b8b604051610ba299989796959493929190614c7b565b60405180910390a35050505050565b600b546001600160a01b03163314610bdb5760405162461bcd60e51b81526004016104a090614df9565b610be8816040015161237a565b15610c055760405162461bcd60e51b81526004016104a090615d6b565b6107d06001600160401b0316610c238260c001518360a00151612cff565b6001600160401b03161115610c4a5760405162461bcd60e51b81526004016104a0906156cf565b6000610c5582612d36565b60008181526004602052604090205490915060ff1615610c875760405162461bcd60e51b81526004016104a090615827565b610c8f614140565b600083516001811115610c9e57fe5b14610cbf576080830151610cba9060019063ffffffff611b6216565b610cd4565b610cd483606001516106d58560200151612d74565b90506000610cfc8460c001518560a001516001600160401b0316612e0190919063ffffffff16565b90506000610d0e828460600151611cbb565b60a08601516040808801516001600160a01b03908116600090815260066020908152838220898201519093168252919091529081205492935091610d63916001600160401b039091169063ffffffff612e0116565b90506000610d75828660600151611cbb565b6040888101516001600160a01b039081166000908152600660208181528483208b820180518616855290825285842080546001600160401b0319166001600160401b038b81169190911790915560c08f0151600c548716865293835286852091519095168452905292902054929350610def921690612e43565b600c546001600160a01b0390811660009081526006602090815260408083208a830180518616855292529182902080546001600160401b0319166001600160401b0395909516949094179093556005548a82015193519151636ce5768960e11b815292169263d9caed1292610e6992908890600401614c24565b600060405180830381600087803b158015610e8357600080fd5b505af1158015610e97573d6000803e3d6000fd5b505050600087815260046020908152604091829020805460ff19166001179055870151898201518883015160a08c015193516001600160a01b03938416955091909216927f6960a1f64ecf9da0d1d1bcbfa3dd27f8c1c60de69b13faa28127dafa36c111e492610f0d9290919088908890614d5f565b60405180910390a350505050505050565b336001600160a01b037f000000000000000000000000b2d3731fd28309c399774fb26f993697c174f4511614610f665760405162461bcd60e51b81526004016104a090615779565b6001600160a01b038116610f8c5760405162461bcd60e51b81526004016104a090615c07565b6000546001600160a01b0382811691161415610fba5760405162461bcd60e51b81526004016104a09061502e565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146110065760405162461bcd60e51b81526004016104a0906159ba565b6110518383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392505063ffffffff612e74169050565b826001600160a01b03167f7f63a63f4a2ea318da0b031794d0b5a3183a1b2de54e053ceb17cabdba031735838360405161108c929190614d11565b60405180910390a2505050565b6110a1614140565b6110eb84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019392508691505063ffffffff611d3b16565b949350505050565b6000546001600160a01b0316331461111d5760405162461bcd60e51b81526004016104a0906159ba565b6001600160a01b0381166111435760405162461bcd60e51b81526004016104a090615c07565b600c546001600160a01b03828116911614156111715760405162461bcd60e51b81526004016104a0906157a7565b600c80546001600160a01b038381166001600160a01b03198316179092556040519116907f9f4f5dce3c4d197b5d7496cb96e25f0a89809167195964b0daa3ef5fed63c00a906107bb9083908590614c61565b6111cd3361237a565b6111e95760405162461bcd60e51b81526004016104a0906153f3565b6111f1614140565b61120260018363ffffffff611b6216565b3360009081526006602090815260408083206001600160a01b038716845290915281205460608301519293506001600160401b031691611243908390611cbb565b9050600081116112655760405162461bcd60e51b81526004016104a090615d92565b3360008181526006602090815260408083206001600160a01b03898116855292529182902080546001600160401b03191690556005549151636ce5768960e11b815291169163d9caed12916112c1919088908690600401614c24565b600060405180830381600087803b1580156112db57600080fd5b505af11580156112ef573d6000803e3d6000fd5b50505050836001600160a01b0316336001600160a01b03167f02e03f232c88c87047c5a9cfbad1213b842503fee6a002ec2eec9f5a64725bf48560400151856000806040516113419493929190614d5f565b60405180910390a350505050565b61135b33600034611fab565b565b336001600160a01b037f000000000000000000000000b2d3731fd28309c399774fb26f993697c174f45116146113a55760405162461bcd60e51b81526004016104a090615779565b600080546001600160a01b0319169055565b6000818152600860205260409020546001600160401b03165b919050565b6000546001600160a01b031633146113ff5760405162461bcd60e51b81526004016104a0906159ba565b6001600160a01b0381166114255760405162461bcd60e51b81526004016104a090615c07565b600b546001600160a01b03828116911614156114535760405162461bcd60e51b81526004016104a090615888565b600b80546001600160a01b038381166001600160a01b03198316179092556040519116907fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39906107bb9083908590614c61565b60006114b182612d74565b90506114bb613089565b6001600160401b0316816001600160401b0316106114eb5760405162461bcd60e51b81526004016104a0906153b2565b3360009081526007602052604090205460ff161561157457336000908152600760205260409020546001600160401b0380831661010090920416106115425760405162461bcd60e51b81526004016104a090614e9d565b336000908152600760205260409020600101544310156115745760405162461bcd60e51b81526004016104a090615adf565b600a546040805160608101825260018082526001600160401b0385811660208085019182524390960184860181815233600081815260079099529787902095518654935160ff199094169015151768ffffffffffffffff001916610100939094169290920292909217845551929091019190915590519091907f10cf19671b43c88b1f02d4e94932d7ffaa89c7278bc5b8868fa7b7676210809b9061108c90869086908690615e78565b60006001600160a01b0383166116465760405162461bcd60e51b81526004016104a090615c07565b506001600160a01b0391821660009081526006602090815260408083209390941682529190915220546001600160401b031690565b6001600160a01b0382166116a15760405162461bcd60e51b81526004016104a090615738565b6116ac338383611fab565b5050565b3360009081526009602052604090205460ff16156116e05760405162461bcd60e51b81526004016104a090615503565b6040805180820182526001808252600a80544390810160208086019182523360008181526009909252908790209551865460ff19169015151786559051949093019390935554925190927fd60f9f7b2f1a208268475a927bd727c4e198fc8b40aab3004ebcc2bc78ca84809261175892910190615ea2565b60405180910390a2565b60006001600160a01b03841661178a5760405162461bcd60e51b81526004016104a090615c07565b60006117cf84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506106d59250611d33915050565b6020908101516001600160a01b03808816600090815260068452604080822092909316815292529020546001600160401b03169150509392505050565b611814614140565b6001600160a01b0384811660009081526020878152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f81018590048502870185018652808752919592948601939091908301828280156118d75780601f106118ac576101008083540402835291602001916118d7565b820191906000526020600020905b8154815290600101906020018083116118ba57829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091506119335760405162461bcd60e51b81526004016104a09061522b565b8060800151156119555760405162461bcd60e51b81526004016104a090615857565b611963816040015184613096565b61197f5760405162461bcd60e51b81526004016104a090614f09565b8160ff16816060015160ff16146119a85760405162461bcd60e51b81526004016104a090615426565b6001608082018190526001600160401b036103e842021660a08301526001600160a01b0380861660009081526020888152604091829020855181548388015190951661010002610100600160a81b031991151560ff1990961695909517169390931783559084015180518594611a2393908501920190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790556040516001860190611a8d908590614ac2565b9081526040805191829003602090810190922080546001818101835560009283529184902085516003909202018054868601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559184015180518594611b04938501920190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790555050505050565b611b6a614140565b6001600160a01b038216611b8757611b806130ef565b905061065c565b611b8f614140565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015611c525780601f10611c2757610100808354040283529160200191611c52565b820191906000526020600020905b815481529060010190602001808311611c3557829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015611c9f575080608001515b61072f5760405162461bcd60e51b81526004016104a090615976565b600060208260ff161115611ce15760405162461bcd60e51b81526004016104a090615a15565b60088260ff161115611d1057611b806001600160401b03841660ff600719850116600a0a63ffffffff61314816565b61072f6001600160401b03841660ff600885900316600a0a63ffffffff61318216565b426103e80290565b611d43614140565b611d686040518060400160405280600381526020016208aa8960eb1b81525084613096565b15611d7c57611d756130ef565b905061072f565b611d84614140565b60008560010185604051611d989190614ac2565b908152604051908190036020019020541115611f7a5760005b8560010185604051611dc39190614ac2565b9081526040519081900360200190205460ff82161015611f7857836001600160401b03168660010186604051611df99190614ac2565b90815260200160405180910390208260ff1681548110611e1557fe5b60009182526020909120600390910201600201546201000090046001600160401b031611611f70578560010185604051611e4f9190614ac2565b90815260200160405180910390208160ff1681548110611e6b57fe5b60009182526020918290206040805160c081018252600393909302909101805460ff811615158452610100908190046001600160a01b031684860152600180830180548551600293821615909402600019011691909104601f8101879004870283018701855280835294959294938601939192909190830182828015611f325780601f10611f0757610100808354040283529160200191611f32565b820191906000526020600020905b815481529060010190602001808311611f1557829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015291505b600101611db1565b505b80518015611f89575080608001515b6110eb5760405162461bcd60e51b81526004016104a090615621565b3b151590565b6001600160a01b03831660009081526009602052604090205460ff1615611fe45760405162461bcd60e51b81526004016104a090615d6b565b611fec614140565b611ffd60018463ffffffff611b6216565b9050600061200f8383606001516131c1565b90506000816001600160401b03161161203a5760405162461bcd60e51b81526004016104a090615168565b600061204a828460600151611cbb565b90506001600160a01b0385161561206657612066868683613256565b60055461207d906001600160a01b031686836133e9565b6001600160a01b0380871660009081526006602090815260408083209389168352929052908120546120be906001600160401b03168463ffffffff612e4316565b905060006120d0828660600151611cbb565b6001600160a01b038981166000908152600660209081526040808320938c16835292905281902080546001600160401b0319166001600160401b03868116919091179091556005805467ffffffffffffffff60a01b198116600160a01b9182900484166001019093160291909117905586810151905191925061215291614ac2565b6040518091039020876001600160a01b0316896001600160a01b03167fc2813a84168de59cf710030ba2acb13031b567b8ba76f70e8ab0782aa9469a7a600560149054906101000a90046001600160401b031689604001518988886040516121be959493929190615ecd565b60405180910390a45050505050505050565b60208160ff1611156121f45760405162461bcd60e51b81526004016104a09061536b565b6001600160a01b03831615801590612210575061221083611fa5565b61222c5760405162461bcd60e51b81526004016104a090615ab0565b600082511161224d5760405162461bcd60e51b81526004016104a090615bae565b6001600160a01b038316600090815260208590526040902060020154610100900460ff161561228e5760405162461bcd60e51b81526004016104a090615857565b6040805160c08101825260018082526001600160a01b03868116602080850182815285870189815260ff8916606088015260006080880181905260a088018190529384528b83529690922085518154935160ff1990941690151517610100600160a81b03191661010093909416929092029290921781559351805193949361231d938501929190910190614176565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000262010000600160501b03199415156101000261ff001960ff90951660ff199093169290921793909316179290921617905550505050565b6001600160a01b0381166000908152600960205260408120805460ff16801561072f57506001015443101592915050565b80606001516001600160a01b031681604001516001600160a01b031614156123e55760405162461bcd60e51b81526004016104a090615487565b6123ed614140565b61240282600001516106d58660200151612d74565b905061240c614140565b61242183602001516106d58760200151612d74565b905082604001516001600160a01b031682602001516001600160a01b0316148015612465575082606001516001600160a01b031681602001516001600160a01b0316145b6124815760405162461bcd60e51b81526004016104a090615195565b612489614140565b61249e84600001516106d58760200151612d74565b90506124a8614140565b6124bd85602001516106d58860200151612d74565b905084604001516001600160a01b031682602001516001600160a01b0316148015612501575084606001516001600160a01b031681602001516001600160a01b0316145b61251d5760405162461bcd60e51b81526004016104a090615a5c565b84604001516001600160a01b03168561010001516001600160a01b03161480612560575084606001516001600160a01b03168561010001516001600160a01b0316145b61257c5760405162461bcd60e51b81526004016104a090615327565b84604001516001600160a01b03168561012001516001600160a01b031614806125bf575084606001516001600160a01b03168561012001516001600160a01b0316145b6125db5760405162461bcd60e51b81526004016104a0906155dd565b8461012001516001600160a01b03168561010001516001600160a01b031614156126175760405162461bcd60e51b81526004016104a090614fe2565b50505050505050565b600081608001516001600160401b03161161264d5760405162461bcd60e51b81526004016104a090615dc0565b60008160a001516001600160401b03161161267a5760405162461bcd60e51b81526004016104a090615d23565b61268783606001516135ce565b156126d3578060a001516001600160401b03166126ac82608001518560e00151613627565b6001600160401b031610156126d35760405162461bcd60e51b81526004016104a0906158d1565b6126e082606001516135ce565b1561272c578060a001516001600160401b031661270582608001518460e00151613627565b6001600160401b0316111561272c5760405162461bcd60e51b81526004016104a090615b77565b505050565b61273e8260400151613680565b6001600160401b03166127548360200151612d74565b6001600160401b03161161277a5760405162461bcd60e51b81526004016104a090615935565b6127878160400151613680565b6001600160401b031661279d8260200151612d74565b6001600160401b0316116116ac5760405162461bcd60e51b81526004016104a090615252565b60008060006127d286856136fd565b905060006127e086866136fd565b919791965090945050505050565b600081604001516001600160a01b03168261010001516001600160a01b03161461281c578160a00151612822565b81608001515b90506107d06001600160401b031661283f83610140015183612cff565b6001600160401b031611156128665760405162461bcd60e51b81526004016104a09061509b565b600082604001516001600160a01b03168361012001516001600160a01b031614612894578260a0015161289a565b82608001515b90506107d06001600160401b03166128b784610160015183612cff565b6001600160401b031611156128de5760405162461bcd60e51b81526004016104a090614f6e565b82608001516001600160401b031661293d84604001516001600160a01b03168561010001516001600160a01b03161461291c57846101600151612923565b8461014001515b60c08601516001600160401b03169063ffffffff612e4316565b6001600160401b0316146129635760405162461bcd60e51b81526004016104a090614f9b565b8260a001516001600160401b03166129c284606001516001600160a01b03168561010001516001600160a01b0316146129a1578461016001516129a8565b8461014001515b60e08601516001600160401b03169063ffffffff612e4316565b6001600160401b03161461272c5760405162461bcd60e51b81526004016104a090615b2f565b6129f385858361379e565b6129fe83838361379e565b5050505050565b60808101516040808401516001600160a01b03908116600090815260066020908152838220848701519093168252919091522054612a54916001600160401b039091169063ffffffff612e0116565b6040838101516001600160a01b0390811660009081526006602081815284832087860180518616855290825285842080546001600160401b0319166001600160401b0398891617905560c08801518a8701518616855292825285842090519094168352929092529190912054612acb921690612e43565b604084810180516001600160a01b039081166000908152600660208181528583208887015185168452815285832080546001600160401b0319166001600160401b0398891617905560a088015194518416835290815284822060608801519093168252919091529190912054612b42921690612e01565b6040848101516001600160a01b039081166000908152600660208181528483206060880180518616855290825285842080546001600160401b0319166001600160401b0398891617905560e0880151898701518616855292825285842090519094168352929092529190912054612bba921690612e43565b6040838101516001600160a01b03908116600090815260066020818152848320606088015185168452815284832080546001600160401b0319166001600160401b03978816179055610140870151600c54851684529181528483206101008801519094168352929092529190912054612c34921690612e43565b600c80546001600160a01b03908116600090815260066020818152604080842061010089015186168552825280842080546001600160401b0319166001600160401b039889161790556101608801519554851684529181528183206101208801519094168352929092522054612cab921690612e43565b600c546001600160a01b039081166000908152600660209081526040808320610120969096015190931682529390935290912080546001600160401b0319166001600160401b039092169190911790555050565b600061271061065883612d216001600160401b0387168463ffffffff61392216565b6001600160401b03169063ffffffff61397d16565b600080612d42836139bc565b9050612d58818461010001518560400151613a77565b61065c5760405162461bcd60e51b81526004016104a090614dc7565b6000600f604c83901c1660018114612d9e5760405162461bcd60e51b81526004016104a090615072565b610fff60301b601084901c1665ffff00000000603085901c1663ffffffff606086901c168183178117612df6650b1d069b54006127106001600160801b038416046001600160401b0316612e0190919063ffffffff16565b979650505050505050565b600061072f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613aa7565b60008282016001600160401b03808516908216101561072f5760405162461bcd60e51b81526004016104a0906150c8565b612e7c614140565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015612f3f5780601f10612f1457610100808354040283529160200191612f3f565b820191906000526020600020905b815481529060010190602001808311612f2257829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015612f8c575080608001515b612fa85760405162461bcd60e51b81526004016104a0906151e8565b612fcd826040518060400160405280600381526020016208aa8960eb1b815250613096565b15612fea5760405162461bcd60e51b81526004016104a090615131565b426103e89081026001600160401b031660a08301526040516001860190613012908590614ac2565b9081526040805191829003602090810190922080546001818101835560009283529184902086516003909202018054878601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559185015180518694611b04938501920190614176565b4262015180016103e80290565b6000816040516020016130a99190614ac2565b60405160208183030381529060405280519060200120836040516020016130d09190614ac2565b6040516020818303038152906040528051906020012014905092915050565b6130f7614140565b506040805160c08101825260018082526000602080840182905284518086018652600381526208aa8960eb1b918101919091529383019390935260126060830152608082015260a081019190915290565b6000826131575750600061065c565b8282028284828161316457fe5b041461072f5760405162461bcd60e51b81526004016104a09061568e565b600061072f83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250613ae6565b600060208260ff1611156131e75760405162461bcd60e51b81526004016104a090615a15565b600060088360ff1611156132165761320f8460ff600719860116600a0a63ffffffff61318216565b9050613233565b6132308460ff600886900316600a0a63ffffffff61314816565b90505b600160401b811061072f5760405162461bcd60e51b81526004016104a090615701565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190613285903090600401614c10565b60206040518083038186803b15801561329d57600080fd5b505afa1580156132b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132d5919061497d565b6040516323b872dd60e01b81529091506001600160a01b038416906323b872dd9061330890879030908790600401614c24565b600060405180830381600087803b15801561332257600080fd5b505af1158015613336573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a0823190613369903090600401614c10565b60206040518083038186803b15801561338157600080fd5b505afa158015613395573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133b9919061497d565b9050826133cc828463ffffffff613b1d16565b146129fe5760405162461bcd60e51b81526004016104a090614e2b565b6001600160a01b03821661343d576040516001600160a01b0384169082156108fc029083906000818181858888f193505050506134385760405162461bcd60e51b81526004016104a0906159e8565b61272c565b6040516370a0823160e01b81526000906001600160a01b038416906370a082319061346c908790600401614c10565b60206040518083038186803b15801561348457600080fd5b505afa158015613498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134bc919061497d565b60405163a9059cbb60e01b81529091506001600160a01b0384169063a9059cbb906134ed9087908690600401614c48565b600060405180830381600087803b15801561350757600080fd5b505af115801561351b573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a082319061354e908890600401614c10565b60206040518083038186803b15801561356657600080fd5b505afa15801561357a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061359e919061497d565b9050826135b1828463ffffffff613b1d16565b146129fe5760405162461bcd60e51b81526004016104a090615c82565b600060018260068111156135de57fe5b14806135f5575060028260068111156135f357fe5b145b8061360b5750600482600681111561360957fe5b145b8061065c575060065b82600681111561362057fe5b1492915050565b60006305f5e1008161365b8261364f6001600160401b0388811690881663ffffffff61314816565b9063ffffffff61318216565b9050600160401b81106106585760405162461bcd60e51b81526004016104a090615c37565b6001600160a01b03811660009081526007602052604081205460ff1680156136c357506001600160a01b0382166000908152600760205260409020600101544310155b156136f557506001600160a01b03811660009081526007602052604090205461010090046001600160401b03166113d0565b506000919050565b6000806137138484600001518560200151613b5f565b905061372981856101a001518660400151613a77565b60008560800151600181111561373b57fe5b1461375e57604051806060016040528060278152602001615f9d60279139613778565b604051806060016040528060268152602001615f77602691395b906137965760405162461bcd60e51b81526004016104a09190614d4c565b509392505050565b60008281526003602052604090205460ff16156137cd5760405162461bcd60e51b81526004016104a090615908565b60008360c0015115613839576137e68460600151613d01565b6138025760405162461bcd60e51b81526004016104a090615294565b60008381526008602052604090205460a0830151613832916001600160401b03918216911663ffffffff612e4316565b905061386c565b6000838152600860205260409020546080830151613869916001600160401b03918216911663ffffffff612e4316565b90505b8360a001516001600160401b0316816001600160401b031611156138a25760405162461bcd60e51b81526004016104a090615664565b8360a001516001600160401b0316816001600160401b031610156138ec57600083815260086020526040902080546001600160401b0319166001600160401b0383161790556108dd565b5050600090815260086020908152604080832080546001600160401b031916905560039091529020805460ff1916600117905550565b60006001600160401b03831661393a5750600061065c565b8282026001600160401b03808416908086169083168161395657fe5b046001600160401b03161461072f5760405162461bcd60e51b81526004016104a09061568e565b600061072f83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b815250613d34565b602081015160408201516000919082845160018111156139d857fe5b14613a065783608001516040516020016139f291906149eb565b604051602081830303815290604052613a2b565b8360600151604051602001613a1b9190614ac2565b6040516020818303038152906040525b613a418560a001516001600160401b0316613d7c565b8560e00151604051602001613a5a959493929190614b0f565b604051602081830303815290604052805190602001209050919050565b6000816001600160a01b0316613a95613a8f86613e78565b85613e8b565b6001600160a01b031614949350505050565b6000836001600160401b0316836001600160401b031611158290613ade5760405162461bcd60e51b81526004016104a09190614d4c565b505050900390565b60008183613b075760405162461bcd60e51b81526004016104a09190614d4c565b506000838581613b1357fe5b0495945050505050565b600061072f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613faf565b6000836000015160ff16600114613b885760405162461bcd60e51b81526004016104a0906154ce565b835160208501516040860151613b9e8686613fd3565b87606001516006811115613bae57fe5b88608001516001811115613bbe57fe5b613bd48a60a001516001600160401b0316613d7c565b604051602001613bea9796959493929190614b7d565b6040516020818303038152906040528460c0015160008660e001516001600160401b031611613c285760405180602001604052806000815250613c3e565b613c3e8660e001516001600160401b0316613d7c565b60008761010001516001600160401b031611613c695760405180602001604052806000815250613c80565b613c808761010001516001600160401b0316613d7c565b8761012001518861014001516003811115613c9757fe5b8961016001516003811115613ca857fe5b8a6101800151604051602001613cc49796959493929190614a03565b60408051601f1981840301815290829052613ce29291602001614a94565b6040516020818303038152906040528051906020012090509392505050565b600080826006811115613d1057fe5b1480613d2757506003826006811115613d2557fe5b145b8061065c57506005613614565b6000816001600160401b038416613d5e5760405162461bcd60e51b81526004016104a09190614d4c565b506000836001600160401b0316856001600160401b031681613b1357fe5b60608160005b8115613d9657600101600a82049150613d82565b6009811015613da3575060095b6001016060816001600160401b0381118015613dbe57600080fd5b506040519080825280601f01601f191660200182016040528015613de9576020820181803683370190505b509050815b8015613e6f5780830360081415613e2f57602e60f81b826001830381518110613e1357fe5b60200101906001600160f81b031916908160001a905350613e66565b600a860660300160f81b826001830381518110613e4857fe5b60200101906001600160f81b031916908160001a905350600a860495505b60001901613dee565b50949350505050565b600081604051602001613a5a9190614ade565b60008151604114613eae5760405162461bcd60e51b81526004016104a090614f37565b60208201516040830151606084015160001a6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03821115613ef65760405162461bcd60e51b81526004016104a0906152e5565b8060ff16601b14158015613f0e57508060ff16601c14155b15613f2b5760405162461bcd60e51b81526004016104a090615532565b600060018783868660405160008152602001604052604051613f509493929190614cf3565b6020604051602081039080840390855afa158015613f72573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613fa55760405162461bcd60e51b81526004016104a090614d95565b9695505050505050565b60008184841115613ade5760405162461bcd60e51b81526004016104a09190614d4c565b604080518082019091526001808252602d60f81b60208301528251845160609386939092869286929101016001600160401b038111801561401357600080fd5b506040519080825280601f01601f19166020018201604052801561403e576020820181803683370190505b5090506000805b85518210156140995785828151811061405a57fe5b602001015160f81c60f81b83828060010193508151811061407757fe5b60200101906001600160f81b031916908160001a905350600190910190614045565b846000815181106140a657fe5b602001015160f81c60f81b8382806001019350815181106140c357fe5b60200101906001600160f81b031916908160001a905350600091505b8351821015614133578382815181106140f457fe5b602001015160f81c60f81b83828060010193508151811061411157fe5b60200101906001600160f81b031916908160001a9053506001909101906140df565b5090979650505050505050565b6040805160c08101825260008082526020820181905260609282018390529181018290526080810182905260a081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106141b757805160ff19168380011785556141e4565b828001600101855582156141e4579182015b828111156141e45782518255916020019190600101906141c9565b506141f09291506141f4565b5090565b61054d91905b808211156141f057600081556001016141fa565b803561065c81615f5e565b8035801515811461065c57600080fd5b600082601f830112614239578081fd5b81356001600160401b0381111561424e578182fd5b614261601f8201601f1916602001615f0c565b915080825283602082850101111561427857600080fd5b8060208401602084013760009082016020015292915050565b80356004811061065c57600080fd5b80356002811061065c57600080fd5b80356007811061065c57600080fd5b60008083601f8401126142cf578182fd5b5081356001600160401b038111156142e5578182fd5b6020830191508360208285010111156142fd57600080fd5b9250929050565b60006101c0808385031215614317578182fd5b61432081615f0c565b91505061432d838361448a565b815261433c836020840161445c565b602082015261434e836040840161420e565b604082015261436083606084016142af565b606082015261437283608084016142a0565b60808201526143848360a08401614473565b60a08201526143968360c08401614219565b60c08201526143a88360e08401614473565b60e08201526101006143bc84828501614473565b90820152610120828101356001600160401b03808211156143dc57600080fd5b6143e886838701614229565b8385015261014092506143fd86848701614291565b83850152610160925061441286848701614291565b83850152610180925061442786848701614473565b838501526101a092508285013591508082111561444357600080fd5b5061445085828601614229565b82840152505092915050565b80356001600160801b038116811461065c57600080fd5b80356001600160401b038116811461065c57600080fd5b803560ff8116811461065c57600080fd5b6000602082840312156144ac578081fd5b813561072f81615f5e565b600080604083850312156144c9578081fd5b82356144d481615f5e565b915060208301356144e481615f5e565b809150509250929050565b600080600060408486031215614503578081fd5b833561450e81615f5e565b925060208401356001600160401b03811115614528578182fd5b614534868287016142be565b9497909650939450505050565b600060208284031215614552578081fd5b5035919050565b6000806000806060858703121561456e578182fd5b843561457981615f5e565b935060208501356001600160401b03811115614593578283fd5b61459f878288016142be565b90945092506145b39050866040870161448a565b905092959194509250565b600080604083850312156145d0578182fd5b82356145db81615f5e565b946020939093013593505050565b6000806000604084860312156145fd578081fd5b83356001600160401b03811115614612578182fd5b61461e868287016142be565b909790965060209590950135949350505050565b600080600060408486031215614646578081fd5b83356001600160401b038082111561465c578283fd5b614668878388016142be565b9095509350602086013591508082168214614681578283fd5b50809150509250925092565b6000806000606084860312156146a1578081fd5b83356001600160401b03808211156146b7578283fd5b6146c387838801614304565b945060208601359150808211156146d8578283fd5b6146e487838801614304565b935060408601359150808211156146f9578283fd5b6101c091860180880383131561470d578384fd5b61471683615f0c565b8135935082841115614726578485fd5b61473289858401614229565b81526020820135935082841115614747578485fd5b61475389858401614229565b6020820152614765896040840161420e565b6040820152614777896060840161420e565b60608201526147898960808401614473565b608082015261479b8960a08401614473565b60a08201526147ad8960c08401614473565b60c08201526147bf8960e08401614473565b60e082015261010093506147d58985840161420e565b8482015261012093506147ea8985840161420e565b8482015261014093506147ff89858401614473565b84820152610160935061481489858401614473565b84820152610180935061482989858401614473565b848201526101a0935061483e898584016142a0565b84820152809450505050509250925092565b600060208284031215614861578081fd5b81356001600160401b0380821115614877578283fd5b61012091840180860383131561488b578384fd5b61489483615f0c565b61489e87836142a0565b81526148ad876020840161445c565b60208201526148bf876040840161420e565b604082015260608201359350828411156148d7578485fd5b6148e387858401614229565b60608201526148f5876080840161420e565b60808201526149078760a08401614473565b60a08201526149198760c08401614473565b60c082015261492b8760e08401614219565b60e082015261010093508382013583811115614945578586fd5b61495188828501614229565b948201949094529695505050505050565b600060208284031215614973578081fd5b61072f838361445c565b60006020828403121561498e578081fd5b5051919050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600081518084526149d7816020860160208601615f32565b601f01601f19169290920160200192915050565b60609190911b6001600160601b031916815260140190565b600088151560f81b82528751614a20816001850160208c01615f32565b8751908301614a36826001830160208c01615f32565b8751918101614a4c836001830160208c01615f32565b60f897881b6001600160f81b0319908116600192909401918201939093529590961b166002850152505060c01b6001600160c01b0319166003820152600b0195945050505050565b60008351614aa6818460208801615f32565b8351908301614ab9828260208801615f32565b01949350505050565b60008251614ad4818460208701615f32565b9190910192915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b608086901b6001600160801b0319168152606085901b6001600160601b03191660108201528351600090614b4a816024850160208901615f32565b8451908301614b60826024830160208901615f32565b93151560f81b602494909101938401525050602501949350505050565b6001600160f81b031960f889901b811682526001600160801b0319608089901b1660018301526001600160601b0319606088901b166011830152855160009190614bce816025860160208b01615f32565b808401828860f81b166025820152828760f81b16602682015285519250614bfc836027830160208901615f32565b9091016027019a9950505050505050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038a811682528916602082015261012060408201819052600090614ca88382018b6149bf565b8381036060850152614cba818b6149bf565b6001600160401b03998a16608086015297891660a08501525050509290941660c083015260e08201526101000191909152949350505050565b93845260ff9290921660208401526040830152606082015260800190565b6000602082526110eb602083018486614995565b600060408252614d39604083018587614995565b905060ff83166020830152949350505050565b60006020825261072f60208301846149bf565b600060808252614d7260808301876149bf565b6001600160401b0395861660208401529390941660408201526060015292915050565b60208082526018908201527745434453413a20696e76616c6964207369676e617475726560401b604082015260600190565b602080825260189082015277496e76616c69642077616c6c6574207369676e617475726560401b604082015260600190565b60208082526018908201527721b0b63632b91034b9903737ba103234b9b830ba31b432b960411b604082015260600190565b6020808252604c908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f46726f6d207375636365737320776974686f757420657870656374656420626160608201526b6c616e6365206368616e676560a01b608082015260a00190565b60208082526023908201527f4e6f6e63652074696d657374616d7020616c726561647920696e76616c6964616040820152621d195960ea1b606082015260800190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252601490820152730a6f2dac4ded8e640c8de40dcdee840dac2e8c6d60631b604082015260600190565b6020808252601f908201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604082015260600190565b6020808252601390820152724578636573736976652074616b65722066656560681b604082015260600190565b60208082526027908201527f4e6574206261736520706c757320666565206973206e6f7420657175616c20746040820152666f2067726f737360c81b606082015260800190565b6020808252602c908201527f4d616b657220616e642074616b65722066656520617373657473206d7573742060408201526b189948191a5999995c995b9d60a21b606082015260800190565b60208082526024908201527f4d75737420626520646966666572656e742066726f6d2063757272656e7420616040820152633236b4b760e11b606082015260800190565b6020808252600f908201526e135d5cdd081899481d8c4815555251608a1b604082015260600190565b602080825260139082015272457863657373697665206d616b65722066656560681b604082015260600190565b6020808252601b908201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604082015260600190565b6020808252601a908201527914d95b1b081dd85b1b195d08195e1a5d08199a5b985b1a5e995960321b604082015260600190565b6020808252601d908201527f4554482073796d626f6c20726573657276656420666f72204574686572000000604082015260600190565b6020808252601390820152725175616e7469747920697320746f6f206c6f7760681b604082015260600190565b60208082526033908201527f427579206f72646572206d61726b65742073796d626f6c2061646472657373206040820152720e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d606b1b606082015260800190565b60208082526023908201527f526567697374726174696f6e206f6620746f6b656e206e6f742066696e616c696040820152621e995960ea1b606082015260800190565b6020808252600d908201526c2ab735b737bbb7103a37b5b2b760991b604082015260600190565b60208082526022908201527f53656c6c206f72646572206e6f6e63652074696d657374616d7020746f6f206c6040820152616f7760f01b606082015260800190565b60208082526031908201527f4f726465722071756f7465207175616e74697479206f6e6c792076616c696420604082015270666f72206d61726b6574206f726465727360781b606082015260800190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604082015261756560f01b606082015260800190565b60208082526024908201527f4d616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526027908201527f546f6b656e2063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b60208082526021908201527f4e6f6e63652074696d657374616d7020746f6f2066617220696e2066757475726040820152606560f81b606082015260800190565b60208082526019908201527815d85b1b195d08195e1a5d081b9bdd08199a5b985b1a5e9959603a1b604082015260600190565b602080825260159082015274088cac6d2dac2d8e640c8de40dcdee840dac2e8c6d605b1b604082015260600190565b60208082526018908201527714d95b198b5d1c98591a5b99c81b9bdd08185b1b1bddd95960421b604082015260600190565b60208082526027908201527f4261736520616e642071756f746520617373657473206d7573742062652064696040820152661999995c995b9d60ca1b606082015260800190565b6020808252818101527f5369676e617475726520686173682076657273696f6e206d7573742062652031604082015260600190565b60208082526015908201527415d85b1b195d08185b1c9958591e48195e1a5d1959605a1b604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604082015261756560f01b606082015260800190565b6020808252601f908201527f557365206465706f736974457468657220746f206465706f7369742045544800604082015260600190565b6020808252601890820152774d757374206265206c657373207468616e2031207765656b60401b604082015260600190565b60208082526024908201527f54616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526023908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f722073796d604082015262189bdb60ea1b606082015260800190565b60208082526010908201526f13dc99195c881bdd995c999a5b1b195960821b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b602080825260189082015277457863657373697665207769746864726177616c2066656560401b604082015260600190565b6020808252601d908201527f506970207175616e74697479206f766572666c6f77732075696e743634000000604082015260600190565b60208082526021908201527f557365206465706f736974457468657220746f206465706f73697420457468656040820152603960f91b606082015260800190565b60208082526014908201527321b0b63632b91036bab9ba1031329037bbb732b960611b604082015260600190565b60208082526029908201527f4d75737420626520646966666572656e742066726f6d2063757272656e7420666040820152681959481dd85b1b195d60ba1b606082015260800190565b6020808252601e908201527f437573746f6469616e2063616e206f6e6c7920626520736574206f6e63650000604082015260600190565b6020808252601690820152752430b9b41030b63932b0b23c903bb4ba34323930bbb760511b604082015260600190565b602080825260179082015276151bdad95b88185b1c9958591e48199a5b985b1a5e9959604a1b604082015260600190565b60208082526029908201527f4d75737420626520646966666572656e742066726f6d2063757272656e74206460408201526834b9b830ba31b432b960b91b606082015260800190565b6020808252601e908201527f427579206f72646572206c696d69742070726963652065786365656465640000604082015260600190565b60208082526013908201527213dc99195c88191bdd589b1948199a5b1b1959606a1b604082015260600190565b60208082526021908201527f427579206f72646572206e6f6e63652074696d657374616d7020746f6f206c6f6040820152607760f81b606082015260800190565b60208082526024908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f72206164646040820152637265737360e01b606082015260800190565b60208082526014908201527321b0b63632b91036bab9ba1031329030b236b4b760611b604082015260600190565b602080825260139082015272115512081d1c985b9cd9995c8819985a5b1959606a1b604082015260600190565b60208082526027908201527f41737365742063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b60208082526034908201527f53656c6c206f72646572206d61726b65742073796d626f6c2061646472657373604082015273040e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d60631b606082015260800190565b602080825260159082015274496e76616c696420746f6b656e206164647265737360581b604082015260600190565b60208082526030908201527f50726576696f757320696e76616c69646174696f6e206177616974696e67206360408201526f3430b4b710383937b830b3b0ba34b7b760811b606082015260800190565b60208082526028908201527f4e65742071756f746520706c757320666565206973206e6f7420657175616c20604082015267746f2067726f737360c01b606082015260800190565b6020808252601f908201527f53656c6c206f72646572206c696d697420707269636520657863656564656400604082015260600190565b602080825260149082015273125b9d985b1a59081d1bdad95b881cde5b589bdb60621b604082015260600190565b60208082526011908201527015d85b1b195d081b9bdd08195e1a5d1959607a1b604082015260600190565b602080825260169082015275496e76616c69642077616c6c6574206164647265737360501b604082015260600190565b6020808252602b908201527f496d706c6965642071756f746520706970207175616e74697479206f7665726660408201526a1b1bdddcc81d5a5b9d0d8d60aa1b606082015260800190565b60208082526048908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f207375636365737320776974686f75742065787065637465642062616c616e6360608201526765206368616e676560c01b608082015260a00190565b602080825260199082015278109d5e481dd85b1b195d08195e1a5d08199a5b985b1a5e9959603a1b604082015260600190565b60208082526028908201527f51756f7465207175616e74697479206d7573742062652067726561746572207460408201526768616e207a65726f60c01b606082015260800190565b6020808252600d908201526c15d85b1b195d08195e1a5d1959609a1b604082015260600190565b602080825260149082015273139bc818985b185b98d948199bdc88185cdcd95d60621b604082015260600190565b60208082526027908201527f42617365207175616e74697479206d7573742062652067726561746572207468604082015266616e207a65726f60c81b606082015260800190565b60006020825282511515602083015260018060a01b036020840151166040830152604083015160c06060840152615e4160e08401826149bf565b60ff60608601511660808501526080850151151560a08501526001600160401b0360a08601511660c0850152809250505092915050565b6001600160801b039390931683526001600160401b03919091166020830152604082015260600190565b90815260200190565b918252602082015260400190565b6001600160401b0391909116815260200190565b60006001600160401b03808816835260a06020840152615ef060a08401886149bf565b9581166040840152939093166060820152608001525092915050565b6040518181016001600160401b0381118282101715615f2a57600080fd5b604052919050565b60005b83811015615f4d578181015183820152602001615f35565b838111156108dd5750506000910152565b6001600160a01b0381168114615f7357600080fd5b5056fe496e76616c69642077616c6c6574207369676e617475726520666f7220627579206f72646572496e76616c69642077616c6c6574207369676e617475726520666f722073656c6c206f72646572a26469706673582212205dd7ac35c5f7c6f5ec79f4ba79633e7c75138d994328bbe33fa2efc0d21be22d64736f6c63430006080033

Deployed Bytecode Sourcemap

1220:38806:5:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12:1:-1;9;2:12;35081:281:5;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;35081:281:5;;;;;;;;:::i;:::-;;11712:85;;5:9:-1;2:2;;;27:1;24;17:12;2:2;11712:85:5;;;:::i;:::-;;;;;;;;;;;;;;;;37580:151;;5:9:-1;2:2;;;27:1;24;17:12;2:2;37580:151:5;;;:::i;8990:422::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;8990:422:5;;;;;;;;:::i;:::-;;;;;;;;9793:465;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;9793:465:5;;;;;;;;:::i;7507:455::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;7507:455:5;;;;;;;;:::i;6843:249::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;6843:249:5;;;;;;;;:::i;13497:442::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;13497:442:5;;;;;;;;:::i;34477:248::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;34477:248:5;;;;;;;;:::i;22047:187::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;22047:187:5;;;:::i;23190:1171::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;23190:1171:5;;;;;;;;:::i;18097:2154::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;18097:2154:5;;;;;;;;:::i;733:222:7:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;733:222:7;;;;;;;;:::i;35628:209:5:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;35628:209:5;;;;;;;;:::i;36433:215::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;36433:215:5;;;;;;;;:::i;:::-;;;;;;;;8216:368;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;8216:368:5;;;;;;;;:::i;21111:778::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;21111:778:5;;;;;;;;:::i;12509:96::-;;;:::i;1097:74:7:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1097:74:7;;;:::i;12270:178:5:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;12270:178:5;;;;;;;;:::i;:::-;;;;;;;;36930:439;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;36930:439:5;;;;;;;;:::i;16672:1168::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;16672:1168:5;;;;;;;;:::i;10626:238::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;10626:238:5;;;;;;;;:::i;12899:285::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;12899:285:5;;;;;;;;:::i;20557:292::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;20557:292:5;;;:::i;11230:370::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;11230:370:5;;;;;;;;:::i;35081:281::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;;;;;;;;;35217:71:5::1;35257:12;35271:6;;35217:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;35217:14:5::1;::::0;:71;;-1:-1:-1;35279:8:5;;-1:-1:-1;;35217:71:5::1;:39;:71;:::i;:::-;35326:12;-1:-1:-1::0;;;;;35299:58:5::1;;35340:6;;35348:8;35299:58;;;;;;;;;;;;;;;;;35081:281:::0;;;;:::o;11712:85::-;11782:10;;-1:-1:-1;;;;;11782:10:5;11712:85;;:::o;37580:151::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;37656:17:5::1;::::0;37638:50:::1;::::0;::::1;::::0;::::1;::::0;-1:-1:-1;;;;;37656:17:5;;::::1;::::0;::::1;::::0;37638:50:::1;;;;;;;;;;37694:17;:32:::0;;-1:-1:-1;;;;;;37694:32:5::1;::::0;;37580:151::o;8990:422::-;9105:7;-1:-1:-1;;;;;9128:22:5;;9120:57;;;;-1:-1:-1;;;9120:57:5;;;;;;;;;9184:26;;:::i;:::-;9213:59;:14;9254:12;9213:59;:33;:59;:::i;:::-;-1:-1:-1;;;;;9338:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;9385:14;;;;9184:88;;-1:-1:-1;9291:116:5;;-1:-1:-1;;;;;9338:37:5;;;;9291;:116::i;:::-;9278:129;;;8990:422;;;;;:::o;9793:465::-;9914:7;-1:-1:-1;;;;;9937:22:5;;9929:57;;;;-1:-1:-1;;;9929:57:5;;;;;;;;;9993:26;;:::i;:::-;10022:90;10062:11;;10022:90;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;10081:25:5;;-1:-1:-1;10081:23:5;;-1:-1:-1;;10081:25:5:i;:::-;10022:14;;:90;;:32;:90;:::i;:::-;-1:-1:-1;;;;;10178:23:5;;;;;;;:15;:23;;;;;;;;10202:18;;;;10178:43;;;;;;;;;;10231:14;;;;9993:119;;-1:-1:-1;10131:122:5;;-1:-1:-1;;;;;10178:43:5;;;;10131:37;:122::i;:::-;10118:135;;;9793:465;;;;;;:::o;7507:455::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;5957:23:5::1;7627:25;:54;7612:109;;;;-1:-1:-1::0;;;7612:109:5::1;;;;;;;;;7764:23;::::0;;7793:51;;;;7856:101:::1;::::0;::::1;::::0;::::1;::::0;7764:23;;7819:25;;7856:101:::1;;;;;;;;;;398:1:7;7507:455:5::0;:::o;6843:249::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;6928:10:5::1;::::0;-1:-1:-1;;;;;6928:10:5::1;:26:::0;6920:69:::1;;;;-1:-1:-1::0;;;6920:69:5::1;;;;;;;;;7003:32;7022:12;7003:18;:32::i;:::-;6995:60;;;;-1:-1:-1::0;;;6995:60:5::1;;;;;;;;;7062:10;:25:::0;;-1:-1:-1;;;;;;7062:25:5::1;-1:-1:-1::0;;;;;7062:25:5;;;::::1;::::0;;;::::1;::::0;;6843:249::o;13497:442::-;13613:19;13649:81;13691:11;;13649:81;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;13704:25:5;;-1:-1:-1;13704:23:5;;-1:-1:-1;;13704:25:5:i;13649:81::-;:103;;;;-1:-1:-1;;;;;;13779:37:5;;13764:99;;;;-1:-1:-1;;;13764:99:5;;;;;;;;;13870:64;13878:10;13898:12;13913:20;13870:7;:64::i;:::-;13497:442;;;;:::o;34477:248::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;34602:60:5::1;34631:12;34645:6;;34602:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;34602:14:5::1;::::0;:60;;-1:-1:-1;34653:8:5;;-1:-1:-1;;34602:60:5::1;:28;:60;:::i;:::-;34689:12;-1:-1:-1::0;;;;;34673:47:5::1;;34703:6;;34711:8;34673:47;;;;;;;;;22047:187:::0;22110:10;22097:24;;;;:12;:24;;;;;:31;;;22089:61;;;;-1:-1:-1;;;22089:61:5;;;;;;;;;22177:10;22164:24;;;;:12;:24;;;;;;22157:31;;-1:-1:-1;;22157:31:5;;;;;;;;22200:29;;;22164:24;22200:29;22047:187::o;23190:1171::-;37789:17;;-1:-1:-1;;;;;37789:17:5;37775:10;:31;37767:68;;;;-1:-1:-1;;;37767:68:5;;;;;;;;;23362:40:::1;23384:3;:17;;;23362:21;:40::i;:::-;23361:41;23346:97;;;;-1:-1:-1::0;;;23346:97:5::1;;;;;;;;;23465:41;23487:4;:18;;;23465:21;:41::i;:::-;23464:42;23449:99;;;;-1:-1:-1::0;;;23449:99:5::1;;;;;;;;;23590:4;:18;;;-1:-1:-1::0;;;;;23569:39:5::1;:3;:17;;;-1:-1:-1::0;;;;;23569:39:5::1;;;23554:94;;;;-1:-1:-1::0;;;23554:94:5::1;;;;;;;;;23655:35;23673:3;23678:4;23684:5;23655:17;:35::i;:::-;23696:37;23716:3;23721:4;23727:5;23696:19;:37::i;:::-;23739:30;23759:3;23764:4;23739:19;:30::i;:::-;23776:15;23793:16:::0;23813:65:::1;23844:3;23855:4;23867:5;23813:23;:65::i;:::-;23775:103;;;;23884:24;23902:5;23884:17;:24::i;:::-;23915:64;23943:3;23948:7;23957:4;23963:8;23973:5;23915:27;:64::i;:::-;23985:40;24008:3;24013:4;24019:5;23985:22;:40::i;:::-;24138:5;:22;;;24037:319;;;;;;;;;::::0;;;;::::1;::::0;;24109:21;;24037:319;;::::1;::::0;24109:21;24037:319:::1;;;;;;;;;;;24058:3;:17;;;24083:4;:18;;;24168:5;:21;;;24197:5;:22;;;24227:5;:29;;;24264:5;:30;;;24302:5;:17;;;24327:7;24342:8;24037:319;;;;;;;;;;;;;;;;;;;;;;;37841:1;;23190:1171:::0;;;:::o;18097:2154::-;37789:17;;-1:-1:-1;;;;;37789:17:5;37775:10;:31;37767:68;;;;-1:-1:-1;;;37767:68:5;;;;;;;;;18232:47:::1;18254:10;:24;;;18232:21;:47::i;:::-;18231:48;18223:74;;;;-1:-1:-1::0;;;18223:74:5::1;;;;;;;;;6116:8;-1:-1:-1::0;;;;;18318:109:5::1;:69;18336:10;:23;;;18361:10;:25;;;18318:17;:69::i;:::-;-1:-1:-1::0;;;;;18318:109:5::1;;;18303:164;;;;-1:-1:-1::0;;;18303:164:5::1;;;;;;;;;18473:22;18498:39;18526:10;18498:27;:39::i;:::-;18559:42;::::0;;;:26:::1;:42;::::0;;;;;18473:64;;-1:-1:-1;18559:42:5::1;;18558:43;18543:96;;;;-1:-1:-1::0;;;18543:96:5::1;;;;;;;;;18730:26;;:::i;:::-;18794:29;18759:25:::0;;:64:::1;::::0;::::1;;;;;;;:271;;19006:23;::::0;::::1;::::0;18972:58:::1;::::0;:14:::1;::::0;:58:::1;:33;:58;:::i;:::-;18759:271;;;18832:131;18874:10;:22;;;18906:49;18938:10;:16;;;18906:31;:49::i;18832:131::-;18730:300;;19085:29;19117:66;19154:10;:23;;;19117:10;:25;;;-1:-1:-1::0;;;;;19117:29:5::1;;;:66;;;;:::i;:::-;19085:98;;19189:36;19228:84;19273:22;19297:5;:14;;;19228:44;:84::i;:::-;19432:25;::::0;::::1;::::0;19368:31:::1;::::0;;::::1;::::0;-1:-1:-1;;;;;19352:48:5;;::::1;19318:31;19352:48:::0;;;:15:::1;:48;::::0;;;;;;19401:18;;::::1;::::0;19352:68;;::::1;::::0;;;;;;;;;;19189:123;;-1:-1:-1;19318:31:5;19352:106:::1;::::0;-1:-1:-1;;;;;19352:68:5;;::::1;::::0;:106:::1;:79;:106;:::i;:::-;19318:140;;19464:38;19505:86;19550:24;19576:5;:14;;;19505:44;:86::i;:::-;19614:24;::::0;;::::1;::::0;-1:-1:-1;;;;;19598:41:5;;::::1;;::::0;;;:15:::1;:41;::::0;;;;;;19640:25;;::::1;::::0;;19598:68;::::1;::::0;;;;;;;;:95;;-1:-1:-1;;;;;;19598:95:5::1;-1:-1:-1::0;;;;;19598:95:5;;::::1;::::0;;;::::1;::::0;;;19815:23:::1;::::0;::::1;::::0;19772:10:::1;::::0;;::::1;19756:27:::0;;;;;;;;19784:18;;19756:47;;::::1;::::0;;;;;;;;19464:127;;-1:-1:-1;19756:88:5::1;::::0;:47:::1;::::0;:51:::1;:88::i;:::-;19715:10;::::0;-1:-1:-1;;;;;19715:10:5;;::::1;19699:27;::::0;;;:15:::1;:27;::::0;;;;;;;19727:25;;::::1;::::0;;19699:54;::::1;::::0;;;;;;;;:145;;-1:-1:-1;;;;;;19699:145:5::1;-1:-1:-1::0;;;;;19699:145:5;;;::::1;::::0;;;::::1;::::0;;;19862:10:::1;::::0;19890:24;;::::1;::::0;19922:18;;19851:131;;-1:-1:-1;;;19851:131:5;;19862:10;::::1;::::0;19851:31:::1;::::0;:131:::1;::::0;19922:18;19948:28;;19851:131:::1;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24::::0;17:12:::1;2:2;19851:131:5;;;;8:9:-1;5:2;;;45:16;42:1;39::::0;24:38:::1;77:16;74:1;67:27;5:2;-1:-1:::0;;;19989:42:5::1;::::0;;;:26:::1;:42;::::0;;;;;;;;:49;;-1:-1:-1;;19989:49:5::1;20034:4;19989:49;::::0;;20099:18;::::1;::::0;20067:24;;::::1;::::0;20125:12;;::::1;::::0;20145:25:::1;::::0;::::1;::::0;20050:196;;-1:-1:-1;;;;;20050:196:5;;::::1;::::0;-1:-1:-1;20050:196:5;;;::::1;::::0;::::1;::::0;::::1;::::0;20125:12;;20145:25;20178:24;;20210:30;;20050:196:::1;;;;;;;;;;37841:1;;;;;;18097:2154:::0;:::o;733:222:7:-;254:10;-1:-1:-1;;;;;268:6:7;254:20;;246:53;;;;-1:-1:-1;;;246:53:7;;;;;;;;;-1:-1:-1;;;;;802:24:7;::::1;794:59;;;;-1:-1:-1::0;;;794:59:7::1;;;;;;;;;879:6;::::0;-1:-1:-1;;;;;867:18:7;;::::1;879:6:::0;::::1;867:18;;859:67;;;;-1:-1:-1::0;;;859:67:7::1;;;;;;;;;933:6;:17:::0;;-1:-1:-1;;;;;;933:17:7::1;-1:-1:-1::0;;;;;933:17:7;;;::::1;::::0;;;::::1;::::0;;733:222::o;35628:209:5:-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;35732:51:5::1;35762:12;35776:6;;35732:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;35732:14:5::1;::::0;:51;;-1:-1:-1;;35732:51:5::1;:29;:51;::::0;-1:-1:-1;35732:51:5:i:1;:::-;35811:12;-1:-1:-1::0;;;;;35794:38:5::1;;35825:6;;35794:38;;;;;;;;;;;;;;;;35628:209:::0;;;:::o;36433:215::-;36546:20;;:::i;:::-;36583:60;36616:11;;36583:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;36583:14:5;;:60;-1:-1:-1;36629:13:5;;-1:-1:-1;;36583:60:5;:32;:60;:::i;:::-;36576:67;36433:215;-1:-1:-1;;;;36433:215:5:o;8216:368::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;-1:-1:-1;;;;;8293:28:5;::::1;8285:63;;;;-1:-1:-1::0;;;8285:63:5::1;;;;;;;;;8385:10;::::0;-1:-1:-1;;;;;8369:26:5;;::::1;8385:10:::0;::::1;8369:26;;8354:98;;;;-1:-1:-1::0;;;8354:98:5::1;;;;;;;;;8482:10;::::0;;-1:-1:-1;;;;;8498:25:5;;::::1;-1:-1:-1::0;;;;;;8498:25:5;::::1;;::::0;;;8535:44:::1;::::0;8482:10;::::1;::::0;8535:44:::1;::::0;::::1;::::0;8482:10;;8511:12;;8535:44:::1;;21111:778:::0;21178:33;21200:10;21178:21;:33::i;:::-;21170:71;;;;-1:-1:-1;;;21170:71:5;;;;;;;;;21248:26;;:::i;:::-;21277:59;:14;21318:12;21277:59;:33;:59;:::i;:::-;21381:10;21342:20;21365:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;21365:41:5;;;;;;;;;;21508:14;;;;21248:88;;-1:-1:-1;;;;;;21365:41:5;;21442:86;;21365:41;;21442:37;:86::i;:::-;21412:116;;21565:1;21543:19;:23;21535:56;;;;-1:-1:-1;;;21535:56:5;;;;;;;;;21613:10;21641:1;21597:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;21597:41:5;;;;;;;;;;;:45;;-1:-1:-1;;;;;;21597:45:5;;;21659:10;;21648:102;;-1:-1:-1;;;21648:102:5;;21659:10;;;21648:31;;:102;;21613:10;21625:12;;21725:19;;21648:102;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;21648:102:5;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;21648:102:5;;;;21807:12;-1:-1:-1;;;;;21762:122:5;21789:10;-1:-1:-1;;;;;21762:122:5;;21827:5;:12;;;21847:13;21868:1;21877;21762:122;;;;;;;;;;;;;;;;;;21111:778;;;;:::o;12509:96::-;12556:44;12564:10;12584:3;12590:9;12556:7;:44::i;:::-;12509:96::o;1097:74:7:-;254:10;-1:-1:-1;;;;;268:6:7;254:20;;246:53;;;;-1:-1:-1;;;246:53:7;;;;;;;;;1162:3:::1;1145:21:::0;;-1:-1:-1;;;;;;1145:21:7::1;::::0;;1097:74::o;12270:178:5:-;12372:6;12395:48;;;:37;:48;;;;;;-1:-1:-1;;;;;12395:48:5;12270:178;;;;:::o;36930:439::-;361:6:7;;-1:-1:-1;;;;;361:6:7;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:7;;;;;;;;;-1:-1:-1;;;;;37015:35:5;::::1;37007:70;;;;-1:-1:-1::0;;;37007:70:5::1;;;;;;;;;37121:17;::::0;-1:-1:-1;;;;;37098:40:5;;::::1;37121:17:::0;::::1;37098:40;;37083:112;;;;-1:-1:-1::0;;;37083:112:5::1;;;;;;;;;37231:17;::::0;;-1:-1:-1;;;;;37254:39:5;;::::1;-1:-1:-1::0;;;;;;37254:39:5;::::1;;::::0;;;37305:59:::1;::::0;37231:17;::::1;::::0;37305:59:::1;::::0;::::1;::::0;37231:17;;37274:19;;37305:59:::1;;16672:1168:::0;16732:20;16755:38;16787:5;16755:31;:38::i;:::-;16732:61;;16978:22;:20;:22::i;:::-;-1:-1:-1;;;;;16962:38:5;:13;-1:-1:-1;;;;;16962:38:5;;16947:102;;;;-1:-1:-1;;;16947:102:5;;;;;;;;;17080:10;17060:31;;;;:19;:31;;;;;:38;;;17056:353;;;17145:10;17125:31;;;;:19;:31;;;;;:45;-1:-1:-1;;;;;17125:61:5;;;:45;;;;;:61;17108:133;;;;-1:-1:-1;;;17108:133:5;;;;;;;;;17286:10;17266:31;;;;:19;:31;;;;;:52;;;17322:12;-1:-1:-1;17266:68:5;17249:153;;;;-1:-1:-1;;;17249:153:5;;;;;;;;;17569:23;;17632:84;;;;;;;;17657:4;17632:84;;;-1:-1:-1;;;;;17632:84:5;;;;;;;;;;17554:12;:38;;;17632:84;;;;;;17618:10;-1:-1:-1;17598:31:5;;;:19;:31;;;;;;;:118;;;;;;-1:-1:-1;;17598:118:5;;;;;;;-1:-1:-1;;17598:118:5;;;;;;;;;;;;;;;;;;;;;;;;;17728:107;;17554:38;;17618:10;17728:107;;;;17775:5;;17632:84;;17554:38;;17728:107;;10626:238;10735:6;-1:-1:-1;;;;;10759:22:5;;10751:57;;;;-1:-1:-1;;;10751:57:5;;;;;;;;;-1:-1:-1;;;;;;10822:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;;;-1:-1:-1;;;;;10822:37:5;;10626:238::o;12899:285::-;-1:-1:-1;;;;;13023:37:5;;13008:101;;;;-1:-1:-1;;;13008:101:5;;;;;;;;;13115:64;13123:10;13143:12;13158:20;13115:7;:64::i;:::-;12899:285;;:::o;20557:292::-;20616:10;20603:24;;;;:12;:24;;;;;:31;;;20602:32;20594:66;;;;-1:-1:-1;;;20594:66:5;;;;;;;;;20694:74;;;;;;;;20712:4;20694:74;;;20739:23;;;20724:12;:38;;;20694:74;;;;;;;20680:10;-1:-1:-1;20667:24:5;;;:12;:24;;;;;;;:101;;;;-1:-1:-1;;20667:101:5;;;;;;;;;;;;;;;;;20820:23;20780:64;;20680:10;;20780:64;;;;20805:38;;;20780:64;;;;;;;;;;20557:292::o;11230:370::-;11345:6;-1:-1:-1;;;;;11367:22:5;;11359:57;;;;-1:-1:-1;;;11359:57:5;;;;;;;;;11423:20;11446:79;11486:11;;11446:79;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;11499:25:5;;-1:-1:-1;11499:23:5;;-1:-1:-1;;11499:25:5:i;11446:79::-;:99;;;;;-1:-1:-1;;;;;11558:23:5;;;;;;;:15;:23;;;;;;:37;;;;;;;;;;;-1:-1:-1;;;;;11558:37:5;;-1:-1:-1;;11230:370:5;;;;;:::o;1488:714:1:-;1638:26;;:::i;:::-;-1:-1:-1;;;;;1667:43:1;;;:20;:43;;;;;;;;;;;;1638:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1638:72:1;;;;;;;;;;;;;;;;;;;;;;;;;1667:43;;1638:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1638:72:1;;;-1:-1:-1;;1638:72:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1638:72:1;;;;;;1724:12;;;;-1:-1:-1;1716:38:1;;;;-1:-1:-1;;;1716:38:1;;;;;;;;;1769:5;:17;;;1768:18;1760:54;;;;-1:-1:-1;;;1760:54:1;;;;;;;;;1828:35;1842:5;:12;;;1856:6;1828:13;:35::i;:::-;1820:68;;;;-1:-1:-1;;;1820:68:1;;;;;;;;;1920:8;1902:26;;:5;:14;;;:26;;;1894:60;;;;-1:-1:-1;;;1894:60:1;;;;;;;;;1981:4;1961:17;;;:24;;;-1:-1:-1;;;;;2047:4:1;2029:15;:22;1991:61;:28;;;:61;-1:-1:-1;;;;;2101:43:1;;;1961:24;2101:43;;;;;;;;;;;;:51;;;;;;;;;;;;;-1:-1:-1;;;;;;2101:51:1;;;-1:-1:-1;;2101:51:1;;;;;;;;;;;;;;;;;;;;1961:5;;2101:51;;;;;;;;;:::i;:::-;-1:-1:-1;2101:51:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2101:51:1;;;-1:-1:-1;;;;;;2101:51:1;;;;;-1:-1:-1;;2101:51:1;;;;-1:-1:-1;;2101:51:1;;;;;;;;;;;;;;;;;;;2158:27;;2101:51;2158:19;;;:27;;2178:6;;2158:27;;;;;;;;;;;;;;;;;;;;27:10:-1;;39:1;23:18;;;45:23;;-1:-1;2158:39:1;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2158:39:1;;;-1:-1:-1;;;;;;2158:39:1;;;-1:-1:-1;;2158:39:1;;;;;;;;;;;;;;;;;;;;;;;2191:5;;2158:39;;;;;;;;:::i;:::-;-1:-1:-1;2158:39:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2158:39:1;;;-1:-1:-1;;;;;;2158:39:1;;;;;-1:-1:-1;;2158:39:1;;;;-1:-1:-1;;2158:39:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1488:714:1:o;2953:405::-;3060:20;;:::i;:::-;-1:-1:-1;;;;;3094:28:1;;3090:69;;3139:13;:11;:13::i;:::-;3132:20;;;;3090:69;3165:26;;:::i;:::-;-1:-1:-1;;;;;3194:34:1;;;:20;:34;;;;;;;;;;;;3165:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3165:63:1;;;;;;;;;;;;;;;;;;;;;;;;;3194:34;;3165:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;3165:63:1;;;-1:-1:-1;;3165:63:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3165:63:1;;;;;;3249:12;;;;-1:-1:-1;3249:33:1;;;;;3265:5;:17;;;3249:33;3234:100;;;;-1:-1:-1;;;3234:100:1;;;;;;;;326:472:3;431:7;473:2;456:13;:19;;;;448:71;;;;-1:-1:-1;;;448:71:3;;;;;;;;;634:1;618:13;:17;;;614:106;;;652:61;-1:-1:-1;;;;;652:23:3;;680:32;-1:-1:-1;;694:17:3;;680:32;688:2;680:32;652:61;:27;:61;:::i;614:106::-;732:61;-1:-1:-1;;;;;732:23:3;;760:32;774:1;:17;;;760:32;768:2;760:32;732:61;:27;:61;:::i;38433:156:5:-;38552:15;38527:4;38545:39;38433:156;:::o;3722:691:1:-;3854:20;;:::i;:::-;3886:28;;;;;;;;;;;;;;-1:-1:-1;;;3886:28:1;;;3907:6;3886:13;:28::i;:::-;3882:69;;;3931:13;:11;:13::i;:::-;3924:20;;;;3882:69;3957:26;;:::i;:::-;4030:1;3993:4;:19;;4013:6;3993:27;;;;;;;;;;;;;;;;;;;;;:34;:38;3989:296;;;4046:7;4041:238;4063:4;:19;;4083:6;4063:27;;;;;;;;;;;;;;;;;;;;;:34;4059:38;;;;4041:238;;;4186:13;-1:-1:-1;;;;;4129:70:1;:4;:19;;4149:6;4129:27;;;;;;;;;;;;;;;;;;;;4157:1;4129:30;;;;;;;;;;;;;;;;;;;;;;;:53;;;;;;-1:-1:-1;;;;;4129:53:1;:70;4114:157;;4230:4;:19;;4250:6;4230:27;;;;;;;;;;;;;;;;;;;;4258:1;4230:30;;;;;;;;;;;;;;;;;;;4222:38;;;;;;;;4230:30;;;;;;;;4222:38;;;;;;;;;;;;;;-1:-1:-1;;;;;4222:38:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4222:38:1;;;;;;;;;;;;;;;;;;;;;;;;;4230:30;;4222:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;4222:38:1;;;-1:-1:-1;;4222:38:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4222:38:1;;;;;;;-1:-1:-1;4114:157:1;4099:3;;4041:238;;;;3989:296;4305:12;;:33;;;;;4321:5;:17;;;4305:33;4290:99;;;;-1:-1:-1;;;4290:99:1;;;;;;;;718:413:0;1078:20;1116:8;;;718:413::o;13943:2369:5:-;-1:-1:-1;;;;;14297:20:5;;;;;;:12;:20;;;;;:27;;;14296:28;14288:54;;;;-1:-1:-1;;;14288:54:5;;;;;;;;;14349:26;;:::i;:::-;14378:59;:14;14419:12;14378:59;:33;:59;:::i;:::-;14349:88;;14443:21;14467:93;14512:20;14540:5;:14;;;14467:37;:93::i;:::-;14443:117;;14591:1;14574:14;-1:-1:-1;;;;;14574:18:5;;14566:50;;;;-1:-1:-1;;;14566:50:5;;;;;;;;;14983:49;15035:76;15080:14;15096:5;:14;;;15035:44;:76::i;:::-;14983:128;-1:-1:-1;;;;;;15359:28:5;;;15355:181;;15397:132;15434:6;15457:12;15480:41;15397:27;:132::i;:::-;15618:10;;15585:118;;-1:-1:-1;;;;;15618:10:5;15636:12;15656:41;15585:25;:118::i;:::-;-1:-1:-1;;;;;15744:23:5;;;15710:31;15744:23;;;:15;:23;;;;;;;;:37;;;;;;;;;;;;:69;;-1:-1:-1;;;;;15744:37:5;15793:14;15744:69;:41;:69;:::i;:::-;15710:103;;15819:38;15860:86;15905:24;15931:5;:14;;;15860:44;:86::i;:::-;-1:-1:-1;;;;;16008:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;;:64;;-1:-1:-1;;;;;;16008:64:5;-1:-1:-1;;;;;16008:64:5;;;;;;;;;;16078:13;:15;;-1:-1:-1;;;;16078:15:5;;-1:-1:-1;;;16078:15:5;;;;;;-1:-1:-1;16078:15:5;;;;;;;;;;;16177:12;;;;16105:202;;15819:127;;-1:-1:-1;16105:202:5;;;;;;;;;;;;16157:12;-1:-1:-1;;;;;16105:202:5;16143:6;-1:-1:-1;;;;;16105:202:5;;16122:13;;;;;;;;;-1:-1:-1;;;;;16122:13:5;16197:5;:12;;;16217:14;16239:24;16271:30;16105:202;;;;;;;;;;;;;;;;;;;13943:2369;;;;;;;;:::o;623:861:1:-;782:2;770:8;:14;;;;762:66;;;;-1:-1:-1;;;762:66:1;;;;;;;;;-1:-1:-1;;;;;849:27:1;;;;;;:72;;;880:41;907:12;880:18;:41::i;:::-;834:124;;;;-1:-1:-1;;;834:124:1;;;;;;;;;1093:1;1076:6;1070:20;:24;1062:57;;;;-1:-1:-1;;;1062:57:1;;;;;;;;;-1:-1:-1;;;;;1141:43:1;;:20;:43;;;;;;;;;;:55;;;;;;;;1140:56;1125:110;;;;-1:-1:-1;;;1125:110:1;;;;;;;;;1288:191;;;;;;;;1318:4;1288:191;;;-1:-1:-1;;;;;1288:191:1;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1288:191:1;;;;;;;;;;;;1242:43;;;;;;;;;;:237;;;;;;-1:-1:-1;;1242:237:1;;;;;;;-1:-1:-1;;;;;;1242:237:1;;;;;;;;;;;;;;;;;;;;1288:191;;1242:43;:237;;;;;;;;;;;:::i;:::-;-1:-1:-1;1242:237:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1242:237:1;;;-1:-1:-1;;;;;;1242:237:1;;;;;-1:-1:-1;;1242:237:1;;;;-1:-1:-1;;1242:237:1;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;623:861:1:o;22238:202:5:-;-1:-1:-1;;;;;22346:20:5;;22308:4;22346:20;;;:12;:20;;;;;22379:11;;;;:56;;;;-1:-1:-1;22394:25:5;;;22423:12;-1:-1:-1;22394:41:5;;22372:63;-1:-1:-1;;22238:202:5:o;28054:1929::-;28238:5;:23;;;-1:-1:-1;;;;;28212:49:5;:5;:22;;;-1:-1:-1;;;;;28212:49:5;;;28197:119;;;;-1:-1:-1;;;28197:119:5;;;;;;;;;28352:33;;:::i;:::-;28388:117;28428:5;:21;;;28457:42;28489:3;:9;;;28457:31;:42::i;28388:117::-;28352:153;;28511:34;;:::i;:::-;28548:118;28588:5;:22;;;28618:42;28650:3;:9;;;28618:31;:42::i;28548:118::-;28511:155;;28716:5;:22;;;-1:-1:-1;;;;;28687:51:5;:12;:25;;;-1:-1:-1;;;;;28687:51:5;;:116;;;;;28780:5;:23;;;-1:-1:-1;;;;;28750:53:5;:13;:26;;;-1:-1:-1;;;;;28750:53:5;;28687:116;28672:198;;;;-1:-1:-1;;;28672:198:5;;;;;;;;;28907:34;;:::i;:::-;28944:118;28984:5;:21;;;29013:43;29045:4;:10;;;29013:31;:43::i;28944:118::-;28907:155;;29068:35;;:::i;:::-;29106:119;29146:5;:22;;;29176:43;29208:4;:10;;;29176:31;:43::i;29106:119::-;29068:157;;29276:5;:22;;;-1:-1:-1;;;;;29246:52:5;:13;:26;;;-1:-1:-1;;;;;29246:52:5;;:118;;;;;29341:5;:23;;;-1:-1:-1;;;;;29310:54:5;:14;:27;;;-1:-1:-1;;;;;29310:54:5;;29246:118;29231:201;;;;-1:-1:-1;;;29231:201:5;;;;;;;;;29512:5;:22;;;-1:-1:-1;;;;;29482:52:5;:5;:26;;;-1:-1:-1;;;;;29482:52:5;;:117;;;;29576:5;:23;;;-1:-1:-1;;;;;29546:53:5;:5;:26;;;-1:-1:-1;;;;;29546:53:5;;29482:117;29467:184;;;;-1:-1:-1;;;29467:184:5;;;;;;;;;29702:5;:22;;;-1:-1:-1;;;;;29672:52:5;:5;:26;;;-1:-1:-1;;;;;29672:52:5;;:117;;;;29766:5;:23;;;-1:-1:-1;;;;;29736:53:5;:5;:26;;;-1:-1:-1;;;;;29736:53:5;;29672:117;29657:184;;;;-1:-1:-1;;;29657:184:5;;;;;;;;;29892:5;:26;;;-1:-1:-1;;;;;29862:56:5;:5;:26;;;-1:-1:-1;;;;;29862:56:5;;;29847:131;;;;-1:-1:-1;;;29847:131:5;;;;;;;;;28054:1929;;;;;;;:::o;29987:909::-;30179:1;30147:5;:29;;;-1:-1:-1;;;;;30147:33:5;;30132:103;;;;-1:-1:-1;;;30132:103:5;;;;;;;;;30289:1;30256:5;:30;;;-1:-1:-1;;;;;30256:34:5;;30241:105;;;;-1:-1:-1;;;30241:105:5;;;;;;;;;30357:31;30374:3;:13;;;30357:16;:31::i;:::-;30353:265;;;30531:5;:30;;;-1:-1:-1;;;;;30415:146:5;:112;30456:5;:29;;;30497:3;:20;;;30415:29;:112::i;:::-;-1:-1:-1;;;;;30415:146:5;;;30398:213;;;;-1:-1:-1;;;30398:213:5;;;;;;;;;30628:32;30645:4;:14;;;30628:16;:32::i;:::-;30624:268;;;30804:5;:30;;;-1:-1:-1;;;;;30687:147:5;:113;30728:5;:29;;;30769:4;:21;;;30687:29;:113::i;:::-;-1:-1:-1;;;;;30687:147:5;;;30670:215;;;;-1:-1:-1;;;30670:215:5;;;;;;;;;29987:909;;;:::o;33209:453::-;33390:46;33418:3;:17;;;33390:27;:46::i;:::-;-1:-1:-1;;;;;33337:99:5;:42;33369:3;:9;;;33337:31;:42::i;:::-;-1:-1:-1;;;;;33337:99:5;;33322:163;;;;-1:-1:-1;;;33322:163:5;;;;;;;;;33560:47;33588:4;:18;;;33560:27;:47::i;:::-;-1:-1:-1;;;;;33506:101:5;:43;33538:4;:10;;;33506:31;:43::i;:::-;-1:-1:-1;;;;;33506:101:5;;33491:166;;;;-1:-1:-1;;;33491:166:5;;;;;;;;32277:346;32429:7;32438;32453:20;32476:34;32499:3;32504:5;32476:22;:34::i;:::-;32453:57;;32516:21;32540:35;32563:4;32569:5;32540:22;:35::i;:::-;32590:12;;;;-1:-1:-1;32277:346:5;;-1:-1:-1;;;;;32277:346:5:o;30900:1373::-;30974:31;31044:5;:22;;;-1:-1:-1;;;;;31008:58:5;:5;:26;;;-1:-1:-1;;;;;31008:58:5;;:135;;31113:5;:30;;;31008:135;;;31075:5;:29;;;31008:135;30974:169;;6049:8;-1:-1:-1;;;;;31164:124:5;:97;31191:5;:28;;;31229:24;31164:17;:97::i;:::-;-1:-1:-1;;;;;31164:124:5;;;31149:174;;;;-1:-1:-1;;;31149:174:5;;;;;;;;;31330:31;31400:5;:22;;;-1:-1:-1;;;;;31364:58:5;:5;:26;;;-1:-1:-1;;;;;31364:58:5;;:135;;31469:5;:30;;;31364:135;;;31431:5;:29;;;31364:135;31330:169;;6049:8;-1:-1:-1;;;;;31520:124:5;:97;31547:5;:28;;;31585:24;31520:17;:97::i;:::-;-1:-1:-1;;;;;31520:124:5;;;31505:174;;;;-1:-1:-1;;;31505:174:5;;;;;;;;;31888:5;:29;;;-1:-1:-1;;;;;31701:216:5;:183;31772:5;:22;;;-1:-1:-1;;;;;31742:52:5;:5;:26;;;-1:-1:-1;;;;;31742:52:5;;:134;;31848:5;:28;;;31742:134;;;31807:5;:28;;;31742:134;31701:27;;;;-1:-1:-1;;;;;31701:31:5;;:183;:31;:183;:::i;:::-;-1:-1:-1;;;;;31701:216:5;;31686:286;;;;-1:-1:-1;;;31686:286:5;;;;;;;;;32182:5;:30;;;-1:-1:-1;;;;;31993:219:5;:185;32065:5;:23;;;-1:-1:-1;;;;;32035:53:5;:5;:26;;;-1:-1:-1;;;;;32035:53:5;;:135;;32142:5;:28;;;32035:135;;;32101:5;:28;;;32035:135;31993:28;;;;-1:-1:-1;;;;;31993:32:5;;:185;:32;:185;:::i;:::-;-1:-1:-1;;;;;31993:219:5;;31978:290;;;;-1:-1:-1;;;31978:290:5;;;;;;;;25927:336;26138:56;26164:8;26174:12;26188:5;26138:25;:56::i;:::-;26200:58;26226:9;26237:13;26252:5;26200:25;:58::i;:::-;25927:336;;;;;:::o;24477:1446::-;24813:29;;;;24751:18;;;;;-1:-1:-1;;;;;24735:35:5;;;;;;;:15;:35;;;;;;;24771:29;;;;24735:66;;;;;;;;;;;:108;;-1:-1:-1;;;;;24735:66:5;;;;:108;:77;:108;:::i;:::-;24682:18;;;;;-1:-1:-1;;;;;24666:35:5;;;;;;;:15;:35;;;;;;;24702:29;;;;;24666:66;;;;;;;;;;:177;;-1:-1:-1;;;;;;24666:177:5;-1:-1:-1;;;;;24666:177:5;;;;;;25038:27;;;;24977:17;;;;24961:34;;;;;;;;;;24996:29;;24961:65;;;;;;;;;;;;;;:105;;:65;;:76;:105::i;:::-;24909:17;;;;;;-1:-1:-1;;;;;24893:34:5;;;;;;;:15;:34;;;;;;;24928:29;;;;24893:65;;;;;;;;;:173;;-1:-1:-1;;;;;;24893:173:5;-1:-1:-1;;;;;24893:173:5;;;;;;25266:30;;;;25204:17;;25188:34;;;;;;;;;;25223:30;;;;25188:66;;;;;;;;;;;;;;:109;;:66;;:77;:109::i;:::-;25135:17;;;;;-1:-1:-1;;;;;25119:34:5;;;;;;;:15;:34;;;;;;;25154:30;;;;;25119:66;;;;;;;;;;:178;;-1:-1:-1;;;;;;25119:178:5;-1:-1:-1;;;;;25119:178:5;;;;;;25498:28;;;;25435:18;;;;25419:35;;;;;;;;;;25455:30;;25419:67;;;;;;;;;;;;;;:108;;:67;;:78;:108::i;:::-;25365:18;;;;;-1:-1:-1;;;;;25349:35:5;;;;;;;:15;:35;;;;;;;25385:30;;;;25349:67;;;;;;;;;:178;;-1:-1:-1;;;;;;25349:178:5;-1:-1:-1;;;;;25349:178:5;;;;;;25715:28;;;;25657:10;;;;25641:27;;;;;;;;25349:178;25669:33;;;25641:62;;;;;;;;;;;;;;:103;;:62;;:73;:103::i;:::-;25592:10;;;-1:-1:-1;;;;;25592:10:5;;;25576:27;;;;:15;:27;;;;;;;;25592:10;25604:33;;;25576:62;;;;;;;;;:168;;-1:-1:-1;;;;;;25576:168:5;-1:-1:-1;;;;;25576:168:5;;;;;;25889:28;;;;25831:10;;;;25815:27;;;;;;;;25843:33;;;;25815:62;;;;;;;;;;;:103;;:62;;:73;:103::i;:::-;25766:10;;-1:-1:-1;;;;;25766:10:5;;;25750:27;;;;:15;:27;;;;;;;;25778:33;;;;;;25750:62;;;;;;;;;;;;:168;;-1:-1:-1;;;;;;25750:168:5;-1:-1:-1;;;;;25750:168:5;;;;;;;;;-1:-1:-1;;24477:1446:5:o;38593:244::-;38680:6;38724:9;38794:38;38826:5;38794:27;-1:-1:-1;;;;;38794:7:5;;38724:9;38794:27;:7;:27;:::i;:::-;-1:-1:-1;;;;;38794:31:5;;:38;:31;:38;:::i;33666:421::-;33775:7;33792:22;33817:46;33852:10;33817:34;:46::i;:::-;33792:71;;33885:129;33922:14;33946:10;:26;;;33982:10;:24;;;33885:27;:129::i;:::-;33870:184;;;;-1:-1:-1;;;33870:184:5;;;;;;;;388:901:11;473:23;596:34;590:2;582:10;;;581:49;655:1;644:12;;636:40;;;;-1:-1:-1;;;636:40:11;;;;;;;;;-1:-1:-1;;;788:2:11;780:10;;;779:49;867:34;861:2;853:10;;;852:49;926:10;934:2;926:10;;;;1013:18;;;:28;;1185:69;1234:14;1216:5;-1:-1:-1;;;;;1192:29:11;;;-1:-1:-1;;;;;1185:41:11;;;:69;;;;:::i;:::-;1166:88;388:901;-1:-1:-1;;;;;;;388:901:11:o;1261:125:9:-;1317:6;1338:43;1342:1;1345;1338:43;;;;;;;;;;;;;;;;;:3;:43::i;859:158::-;915:6;940:5;;;-1:-1:-1;;;;;959:6:9;;;;;;;;951:46;;;;-1:-1:-1;;;951:46:9;;;;;;;;2206:607:1;2326:26;;:::i;:::-;-1:-1:-1;;;;;2355:43:1;;;:20;:43;;;;;;;;;;;;2326:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2326:72:1;;;;;;;;;;;;;;;;;;;;;;;;;2355:43;;2326:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;2326:72:1;;;-1:-1:-1;;2326:72:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2326:72:1;;;;;;2419:12;;;;-1:-1:-1;2419:33:1;;;;;2435:5;:17;;;2419:33;2404:99;;;;-1:-1:-1;;;2404:99:1;;;;;;;;;2518:28;2532:6;2518:28;;;;;;;;;;;;;-1:-1:-1;;;2518:28:1;;;:13;:28::i;:::-;2517:29;2509:71;;;;-1:-1:-1;;;2509:71:1;;;;;;;;;2730:15;2682:4;2730:31;;;-1:-1:-1;;;;;2692:70:1;:28;;;:70;2769:27;;:19;;;;:27;;2789:6;;2769:27;;;;;;;;;;;;;;;;;;;;27:10:-1;;39:1;23:18;;;45:23;;-1:-1;2769:39:1;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2769:39:1;;;-1:-1:-1;;;;;;2769:39:1;;;-1:-1:-1;;2769:39:1;;;;;;;;;;;;;;;;;;;;;;;2802:5;;2769:39;;;;;;;;:::i;39761:263:5:-;39968:15;39854:12;39961:41;39942:4;39960:59;39761:263;:::o;4751:181:1:-;4842:4;4924:1;4907:19;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;4907:19:1;;;4897:30;;;;;;4890:1;4873:19;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;4873:19:1;;;4863:30;;;;;;:64;4856:71;;4751:181;;;;:::o;4519:138::-;4564:20;;:::i;:::-;-1:-1:-1;4599:53:1;;;;;;;;4613:4;4599:53;;;-1:-1:-1;4599:53:1;;;;;;;;;;;;;;;;;-1:-1:-1;;;4599:53:1;;;;;;;;;;;;;;4640:2;4599:53;;;;;;;;;;;;;;;4519:138;:::o;2180:459:8:-;2238:7;2479:6;2475:45;;-1:-1:-1;2508:1:8;2501:8;;2475:45;2542:5;;;2546:1;2542;:5;:1;2565:5;;;;;:10;2557:56;;;;-1:-1:-1;;;2557:56:8;;;;;;;;3101:130;3159:7;3185:39;3189:1;3192;3185:39;;;;;;;;;;;;;-1:-1:-1;;;3185:39:8;;;:3;:39::i;802:673:3:-;914:6;955:2;938:13;:19;;;;930:71;;;;-1:-1:-1;;;930:71:3;;;;;;;;;1008:22;1144:1;1128:13;:17;;;1124:241;;;1172:74;:20;1206:32;-1:-1:-1;;1220:17:3;;1206:32;1214:2;1206:32;1172:74;:24;:74;:::i;:::-;1155:91;;1124:241;;;1284:74;:20;1318:32;1332:1;:17;;;1318:32;1326:2;1318:32;1284:74;:24;:74;:::i;:::-;1267:91;;1124:241;-1:-1:-1;;;1378:14:3;:22;1370:64;;;;-1:-1:-1;;;1370:64:3;;;;;;;;745:608:2;889:37;;-1:-1:-1;;;889:37:2;;865:21;;-1:-1:-1;;;;;889:22:2;;;;;:37;;920:4;;889:37;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;889:37:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;889:37:2;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;889:37:2;;;;;;;;;1043:70;;-1:-1:-1;;;1043:70:2;;865:61;;-1:-1:-1;;;;;;1043:25:2;;;;;:70;;1069:6;;1085:4;;1092:20;;1043:70;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1043:70:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;1143:37:2;;-1:-1:-1;;;1143:37:2;;1120:20;;-1:-1:-1;;;;;;1143:22:2;;;-1:-1:-1;1143:22:2;;:37;;1174:4;;1143:37;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1143:37:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1143:37:2;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;1143:37:2;;;;;;;;;1120:60;-1:-1:-1;1236:20:2;1201:31;1120:60;1218:13;1201:31;:16;:31;:::i;:::-;:55;1186:162;;;;-1:-1:-1;;;1186:162:2;;;;;;;;1548:785;-1:-1:-1;;;;;1682:21:2;;1678:651;;1730:43;;-1:-1:-1;;;;;1730:21:2;;;:43;;;;;1752:20;;1730:43;;;;1752:20;1730:21;:43;;;;;;;1713:99;;;;-1:-1:-1;;;1713:99:2;;;;;;;;;1678:651;;;1857:41;;-1:-1:-1;;;1857:41:2;;1833:21;;-1:-1:-1;;;;;1857:23:2;;;;;:41;;1881:16;;1857:41;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1857:41:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1857:41:2;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;1857:41:2;;;;;;;;;2015:62;;-1:-1:-1;;;2015:62:2;;1833:65;;-1:-1:-1;;;;;;2015:22:2;;;;;:62;;2038:16;;2056:20;;2015:62;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;2015:62:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;2109:41:2;;-1:-1:-1;;;2109:41:2;;2086:20;;-1:-1:-1;;;;;;2109:23:2;;;-1:-1:-1;2109:23:2;;:41;;2133:16;;2109:41;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;2109:41:2;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2109:41:2;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;2109:41:2;;;;;;;;;2086:64;-1:-1:-1;2210:20:2;2175:31;2086:64;2192:13;2175:31;:16;:31;:::i;:::-;:55;2158:164;;;;-1:-1:-1;;;2158:164:2;;;;;;;;37866:308:5;37953:4;37993:21;37980:9;:34;;;;;;;;;:83;;;-1:-1:-1;38037:26:5;38024:9;:39;;;;;;;;;37980:83;:135;;;-1:-1:-1;38086:29:5;38073:9;:42;;;;;;;;;37980:135;:189;;;-1:-1:-1;38138:31:5;38125:44;:9;:44;;;;;;;;;37967:202;37866:308;-1:-1:-1;;37866:308:5:o;38841:573::-;38966:6;39106:5;38966:6;39155:92;39106:5;39155:65;-1:-1:-1;;;;;39155:27:5;;;;39194:25;;39155:65;:38;:65;:::i;:::-;:76;:92;:76;:92;:::i;:::-;39118:129;;-1:-1:-1;;;39268:26:5;:34;39253:108;;;;-1:-1:-1;;;39253:108:5;;;;;;;;39418:339;-1:-1:-1;;;;;39539:34:5;;39512:6;39539:34;;;:19;:34;;;;;:41;;;:122;;;;-1:-1:-1;;;;;;39590:34:5;;;;;;:19;:34;;;;;:55;;;39649:12;-1:-1:-1;39590:71:5;39539:122;39528:210;;;-1:-1:-1;;;;;;39683:34:5;;;;;;:19;:34;;;;;:48;;;;-1:-1:-1;;;;;39683:48:5;39676:55;;39528:210;-1:-1:-1;39751:1:5;39418:339;;;:::o;32627:578::-;32749:7;32764:17;32784:107;32821:5;32834;:21;;;32863:5;:22;;;32784:29;:107::i;:::-;32764:127;;32913:114;32950:9;32969:5;:21;;;33000:5;:19;;;32913:27;:114::i;:::-;33049:19;33035:5;:10;;;:33;;;;;;;;;:136;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32898:279;;;;;-1:-1:-1;;;32898:279:5;;;;;;;;;;-1:-1:-1;33191:9:5;32627:578;-1:-1:-1;;;32627:578:5:o;26358:1671::-;26507:32;;;;:21;:32;;;;;;;;26506:33;26498:65;;;;-1:-1:-1;;;26498:65:5;;;;;;;;;26664:30;26923:5;:23;;;26919:518;;;26973:34;26991:5;:15;;;26973:17;:34::i;:::-;26956:120;;;;-1:-1:-1;;;26956:120:5;;;;;;;;;27154:48;;;;:37;:48;;;;;;27110:30;;;;:100;;-1:-1:-1;;;;;27110:34:5;;;;27154:48;27110:100;:34;:100;:::i;:::-;27084:126;;26919:518;;;27374:48;;;;:37;:48;;;;;;27331:29;;;;:99;;-1:-1:-1;;;;;27331:33:5;;;;27374:48;27331:99;:33;:99;:::i;:::-;27305:125;;26919:518;27485:5;:20;;;-1:-1:-1;;;;;27458:47:5;:23;-1:-1:-1;;;;;27458:47:5;;;27443:94;;;;-1:-1:-1;;;27443:94:5;;;;;;;;;27574:5;:20;;;-1:-1:-1;;;;;27548:46:5;:23;-1:-1:-1;;;;;27548:46:5;;27544:481;;;27678:48;;;;:37;:48;;;;;:74;;-1:-1:-1;;;;;;27678:74:5;-1:-1:-1;;;;;27678:74:5;;;;;27544:481;;;-1:-1:-1;;27923:48:5;;;;:37;:48;;;;;;;;27916:55;;-1:-1:-1;;;;;;27916:55:5;;;27979:21;:32;;;;;:39;;-1:-1:-1;;27979:39:5;-1:-1:-1;27979:39:5;;;-1:-1:-1;26358:1671:5:o;2091:415:9:-;2147:6;-1:-1:-1;;;;;2371:6:9;;2367:35;;-1:-1:-1;2394:1:9;2387:8;;2367:35;2419:5;;;-1:-1:-1;;;;;2438:10:9;;;;:5;;;;;;;;;;;;-1:-1:-1;;;;;2438:10:9;;2430:56;;;;-1:-1:-1;;;2430:56:9;;;;;;;;2937:121;2993:6;3014:39;3018:1;3021;3014:39;;;;;;;;;;;;;-1:-1:-1;;;3014:39:9;;;:3;:39::i;1854:628:10:-;2037:16;;;;2065:24;;;;1960:7;;2037:16;1960:7;2197:25;;:58;;;;;;;;;:169;;2342:10;:23;;;2325:41;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;2325:41:10;;;2197:169;;;2287:10;:22;;;2270:40;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;2270:40:10;;;2197:169;2378:39;2391:10;:25;;;-1:-1:-1;;;;;2378:39:10;:12;:39::i;:::-;2429:10;:30;;;2009:460;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;2009:460:10;;;1990:487;;;;;;1977:500;;1854:628;;;:::o;330:219::-;449:4;538:6;-1:-1:-1;;;;;474:70:10;:60;488:34;517:4;488:28;:34::i;:::-;524:9;474:13;:60::i;:::-;-1:-1:-1;;;;;474:70:10;;;330:219;-1:-1:-1;;;;330:219:10:o;1686:185:9:-;1786:6;1813:1;-1:-1:-1;;;;;1808:6:9;:1;-1:-1:-1;;;;;1808:6:9;;;1816:12;1800:29;;;;;-1:-1:-1;;;1800:29:9;;;;;;;;;;-1:-1:-1;;;1846:5:9;;;1686:185::o;3713:272:8:-;3799:7;3833:12;3826:5;3818:28;;;;-1:-1:-1;;;3818:28:8;;;;;;;;;;;3856:9;3872:1;3868;:5;;;;;;;3713:272;-1:-1:-1;;;;;3713:272:8:o;1321:134::-;1379:7;1405:43;1409:1;1412;1405:43;;;;;;;;;;;;;;;;;:3;:43::i;553:1297:10:-;701:7;731:5;:26;;;:31;;761:1;731:31;716:94;;;;-1:-1:-1;;;716:94:10;;;;;;;;;1008:26;;1048:11;;;;1073:19;;;;1106:40;1122:10;1134:11;1106:15;:40::i;:::-;1166:5;:15;;;1160:22;;;;;;;;1202:5;:10;;;1196:17;;;;;;;;1334:34;1347:5;:20;;;-1:-1:-1;;;;;1334:34:10;:12;:34::i;:::-;978:402;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;978:402:10;;;1422:5;:23;;;1484:1;1459:5;:22;;;-1:-1:-1;;;;;1459:26:10;;:98;;;;;;;;;;;;;;;;;1502:36;1515:5;:22;;;-1:-1:-1;;;;;1502:36:10;:12;:36::i;:::-;1595:1;1571:5;:21;;;-1:-1:-1;;;;;1571:25:10;;:96;;;;;;;;;;;;;;;;;1613:35;1626:5;:21;;;-1:-1:-1;;;;;1613:35:10;:12;:35::i;:::-;1681:5;:19;;;1720:5;:17;;;1714:24;;;;;;;;1758:5;:25;;;1752:32;;;;;;;;1798:5;:17;;;1392:435;;;;;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;1392:435:10;;;;950:887;;;49:4:-1;950:887:10;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;950:887:10;;;829:1016;;;;;;816:1029;;553:1297;;;;;:::o;38178:251:5:-;38266:4;;38293:9;:35;;;;;;;;;:82;;;-1:-1:-1;38351:24:5;38338:9;:37;;;;;;;;;38293:82;:131;;;-1:-1:-1;38398:26:5;38385:39;;3545:328:9;3645:6;3736:12;-1:-1:-1;;;;;3729:5:9;;3721:28;;;;-1:-1:-1;;;3721:28:9;;;;;;;;;;;3755:8;3770:1;-1:-1:-1;;;;;3766:5:9;:1;-1:-1:-1;;;;;3766:5:9;;;;;3829:776:10;3887:13;4068:4;4053:12;4098:59;4105:9;;4098:59;;4124:8;;4148:2;4140:10;;;;4098:59;;;4175:1;4166:6;:10;4162:92;;;-1:-1:-1;4195:1:10;4162:92;4259:8;;4299:20;4259:8;-1:-1:-1;;;;;4322:17:10;;2:2:-1;;;;27:1;24;17:12;2:2;4322:17:10;;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;108:14;4322:17:10;87:42:-1;143:17;;-1:-1;4322:17:10;-1:-1:-1;4299:40:10;-1:-1:-1;4362:6:10;4345:228;4370:5;;4345:228;;4403:1;4394:6;:10;4408:1;4394:15;4390:177;;;4451:2;4438:17;;4421:7;4433:1;4429;:5;4421:14;;;;;;;;;;;:34;-1:-1:-1;;;;;4421:34:10;;;;;;;;;4390:177;;;4533:2;4526:4;:9;4520:2;:16;4507:31;;4490:7;4502:1;4498;:5;4490:14;;;;;;;;;;;:48;-1:-1:-1;;;;;4490:48:10;;;;;;;;-1:-1:-1;4556:2:10;4548:10;;;;4390:177;-1:-1:-1;;4377:3:10;4345:228;;;-1:-1:-1;4592:7:10;3829:776;-1:-1:-1;;;;3829:776:10:o;3396:265:4:-;3465:7;3648:4;3595:58;;;;;;;;;1064:2068;1142:7;1203:9;:16;1223:2;1203:22;1199:94;;1241:41;;-1:-1:-1;;;1241:41:4;;;;;;;;1199:94;1643:4;1628:20;;1622:27;1688:4;1673:20;;1667:27;1741:4;1726:20;;1720:27;1359:9;1712:36;-1:-1:-1;;;;;2646:79:4;;2642:154;;;2741:44;;-1:-1:-1;;;2741:44:4;;;;;;;;2642:154;2810:1;:7;;2815:2;2810:7;;:18;;;;;2821:1;:7;;2826:2;2821:7;;2810:18;2806:93;;;2844:44;;-1:-1:-1;;;2844:44:4;;;;;;;;2806:93;2993:14;3010:24;3020:4;3026:1;3029;3032;3010:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;3010:24:4;;-1:-1:-1;;3010:24:4;;;-1:-1:-1;;;;;;;3052:20:4;;3044:57;;;;-1:-1:-1;;;3044:57:4;;;;;;;;;3119:6;1064:2068;-1:-1:-1;;;;;;1064:2068:4:o;1746:187:8:-;1832:7;1867:12;1859:6;;;;1851:29;;;;-1:-1:-1;;;1851:29:8;;;;;;;;;2832:805:10;3048:10;;;;;;;;;;;;;-1:-1:-1;;;3048:10:10;;;;3212:23;;3187:22;;2944:13;;3004:10;;3048;;3102:11;;2944:13;;3187:48;;:69;-1:-1:-1;;;;;3167:97:10;;2:2:-1;;;;27:1;24;17:12;2:2;3167:97:10;;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;108:14;3167:97:10;87:42:-1;143:17;;-1:-1;3167:97:10;-1:-1:-1;3121:149:10;-1:-1:-1;3277:9:10;;3308:103;3324:15;:22;3320:1;:26;3308:103;;;3386:15;3402:1;3386:18;;;;;;;;;;;;;;;;3361:17;3379:3;;;;;;3361:22;;;;;;;;;;;:43;-1:-1:-1;;;;;3361:43:10;;;;;;;;-1:-1:-1;3348:3:10;;;;;3308:103;;;3468:11;3480:1;3468:14;;;;;;;;;;;;;;;;3443:17;3461:3;;;;;;3443:22;;;;;;;;;;;:39;-1:-1:-1;;;;;3443:39:10;;;;;;;;;3498:1;3494:5;;3489:105;3505:16;:23;3501:1;:27;3489:105;;;3568:16;3585:1;3568:19;;;;;;;;;;;;;;;;3543:17;3561:3;;;;;;3543:22;;;;;;;;;;;:44;-1:-1:-1;;;;;3543:44:10;;;;;;;;-1:-1:-1;3530:3:10;;;;;3489:105;;;-1:-1:-1;3614:17:10;;2832:805;-1:-1:-1;;;;;;;2832:805:10:o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;5:130;72:20;;97:33;72:20;97:33;;295:124;359:20;;94476:13;;94469:21;97992:32;;97982:2;;98038:1;;98028:12;564:440;;665:3;658:4;650:6;646:17;642:27;632:2;;-1:-1;;673:12;632:2;720:6;707:20;-1:-1;;;;;92786:6;92783:30;92780:2;;;-1:-1;;92816:12;92780:2;742:64;92889:9;92870:17;;-1:-1;;92866:33;92957:4;92947:15;742:64;;;733:73;;826:6;819:5;812:21;930:3;92957:4;921:6;854;912:16;;909:25;906:2;;;947:1;;937:12;906:2;96030:6;92957:4;854:6;850:17;92957:4;888:5;884:16;96007:30;96086:1;96068:16;;;92957:4;96068:16;96061:27;888:5;625:379;-1:-1;;625:379;1179:188;1275:20;;98430:1;98420:12;;98410:2;;98446:1;;98436:12;1374:158;1455:20;;98545:1;98535:12;;98525:2;;98561:1;;98551:12;1718:158;1799:20;;98782:1;98772:12;;98762:2;;98798:1;;98788:12;2073:337;;;2188:3;2181:4;2173:6;2169:17;2165:27;2155:2;;-1:-1;;2196:12;2155:2;-1:-1;2226:20;;-1:-1;;;;;2255:30;;2252:2;;;-1:-1;;2288:12;2252:2;2332:4;2324:6;2320:17;2308:29;;2383:3;2332:4;2363:17;2324:6;2349:32;;2346:41;2343:2;;;2400:1;;2390:12;2343:2;2148:262;;;;;;2896:2605;;3008:6;;2996:9;2991:3;2987:19;2983:32;2980:2;;;-1:-1;;3018:12;2980:2;3046:22;3008:6;3046:22;;;3037:31;;;3166:47;3209:3;3185:22;3166:47;;;3148:16;3141:73;3309:49;3354:3;3276:2;3334:9;3330:22;3309:49;;;3276:2;3295:5;3291:16;3284:75;3462:49;3507:3;3429:2;3487:9;3483:22;3462:49;;;3429:2;3448:5;3444:16;3437:75;3611:63;3670:3;3578:2;3650:9;3646:22;3611:63;;;3578:2;3597:5;3593:16;3586:89;3770:63;3829:3;3736;3809:9;3805:22;3770:63;;;3736:3;3756:5;3752:16;3745:89;3939:48;3983:3;3905;3963:9;3959:22;3939:48;;;3905:3;3925:5;3921:16;3914:74;4096:46;4138:3;4062;4118:9;4114:22;4096:46;;;4062:3;4082:5;4078:16;4071:72;4250:48;4294:3;4216;4274:9;4270:22;4250:48;;;4216:3;4236:5;4232:16;4225:74;4371:3;4407:48;4451:3;4371;4431:9;4427:22;4407:48;;;4387:18;;;4380:76;4554:3;4539:19;;;4526:33;-1:-1;;;;;4568:30;;;4565:2;;;3134:1;;4601:12;4565:2;4648:59;4703:3;4694:6;4683:9;4679:22;4648:59;;;4554:3;4632:5;4628:18;4621:87;4776:3;;;4812:70;4878:3;4776;4858:9;4854:22;4812:70;;;4776:3;4796:5;4792:18;4785:98;4959:3;;;4995:78;5069:3;4959;5049:9;5045:22;4995:78;;;4959:3;4979:5;4975:18;4968:106;5142:3;;;5178:48;5222:3;5142;5202:9;5198:22;5178:48;;;5142:3;5162:5;5158:18;5151:76;5327:3;;;;5316:9;5312:19;5299:33;5285:47;;4579:18;5344:6;5341:30;5338:2;;;3134:1;;5374:12;5338:2;;5421:58;5475:3;5466:6;5455:9;5451:22;5421:58;;;5327:3;5405:5;5401:18;5394:86;;;2974:2527;;;;;9982:130;10049:20;;-1:-1;;;;;94756:46;;98993:35;;98983:2;;99042:1;;99032:12;10397:128;10463:20;;-1:-1;;;;;95082:30;;99240:34;;99230:2;;99288:1;;99278:12;10532:126;10597:20;;95195:4;95184:16;;99361:33;;99351:2;;99408:1;;99398:12;10665:241;;10769:2;10757:9;10748:7;10744:23;10740:32;10737:2;;;-1:-1;;10775:12;10737:2;85:6;72:20;97:33;124:5;97:33;;11177:366;;;11298:2;11286:9;11277:7;11273:23;11269:32;11266:2;;;-1:-1;;11304:12;11266:2;85:6;72:20;97:33;124:5;97:33;;;11356:63;-1:-1;11456:2;11495:22;;72:20;97:33;72:20;97:33;;;11464:63;;;;11260:283;;;;;;11550:492;;;;11691:2;11679:9;11670:7;11666:23;11662:32;11659:2;;;-1:-1;;11697:12;11659:2;85:6;72:20;97:33;124:5;97:33;;;11749:63;-1:-1;11877:2;11862:18;;11849:32;-1:-1;;;;;11890:30;;11887:2;;;-1:-1;;11923:12;11887:2;11961:65;12018:7;12009:6;11998:9;11994:22;11961:65;;;11653:389;;11951:75;;-1:-1;11951:75;;-1:-1;;;;11653:389;12049:241;;12153:2;12141:9;12132:7;12128:23;12124:32;12121:2;;;-1:-1;;12159:12;12121:2;-1:-1;493:20;;12115:175;-1:-1;12115:175;12826:643;;;;;12997:2;12985:9;12976:7;12972:23;12968:32;12965:2;;;-1:-1;;13003:12;12965:2;1107:6;1094:20;1119:48;1161:5;1119:48;;;13055:78;-1:-1;13198:2;13183:18;;13170:32;-1:-1;;;;;13211:30;;13208:2;;;-1:-1;;13244:12;13208:2;13282:65;13339:7;13330:6;13319:9;13315:22;13282:65;;;13272:75;;-1:-1;13272:75;-1:-1;13402:51;;-1:-1;13445:7;13384:2;13421:22;;13402:51;;;13392:61;;12959:510;;;;;;;;13476:396;;;13612:2;13600:9;13591:7;13587:23;13583:32;13580:2;;;-1:-1;;13618:12;13580:2;1107:6;1094:20;1119:48;1161:5;1119:48;;;13670:78;13785:2;13824:22;;;;10186:20;;-1:-1;;;13574:298;13879:492;;;;14020:2;14008:9;13999:7;13995:23;13991:32;13988:2;;;-1:-1;;14026:12;13988:2;14084:17;14071:31;-1:-1;;;;;14114:6;14111:30;14108:2;;;-1:-1;;14144:12;14108:2;14182:65;14239:7;14230:6;14219:9;14215:22;14182:65;;;14172:75;;;;-1:-1;14284:2;14323:22;;;;10186:20;;13982:389;-1:-1;;;;13982:389;14378:490;;;;14518:2;14506:9;14497:7;14493:23;14489:32;14486:2;;;-1:-1;;14524:12;14486:2;14582:17;14569:31;-1:-1;;;;;14620:18;14612:6;14609:30;14606:2;;;-1:-1;;14642:12;14606:2;14680:65;14737:7;14728:6;14717:9;14713:22;14680:65;;;14670:75;;-1:-1;14670:75;-1:-1;14782:2;14820:22;;10463:20;;-1:-1;95082:30;;;99240:34;;99230:2;;-1:-1;;99278:12;99230:2;;14790:62;;;;14480:388;;;;;;14875:887;;;;15082:2;15070:9;15061:7;15057:23;15053:32;15050:2;;;-1:-1;;15088:12;15050:2;15146:17;15133:31;-1:-1;;;;;15184:18;15176:6;15173:30;15170:2;;;-1:-1;;15206:12;15170:2;15236:76;15304:7;15295:6;15284:9;15280:22;15236:76;;;15226:86;;15377:2;15366:9;15362:18;15349:32;15335:46;;15184:18;15393:6;15390:30;15387:2;;;-1:-1;;15423:12;15387:2;15453:76;15521:7;15512:6;15501:9;15497:22;15453:76;;;15443:86;;15594:2;15583:9;15579:18;15566:32;15552:46;;15184:18;15610:6;15607:30;15604:2;;;-1:-1;;15640:12;15604:2;5647:6;;15714:22;;5626:19;;;5622:32;-1:-1;5619:2;;;-1:-1;;5657:12;5619:2;5685:22;5647:6;5685:22;;;5781:17;5768:31;5754:45;;15184:18;5811:6;5808:30;5805:2;;;-1:-1;;5841:12;5805:2;5886:59;5941:3;5932:6;5921:9;5917:22;5886:59;;;5868:16;5861:85;15377:2;6036:9;6032:18;6019:32;6005:46;;15184:18;6063:6;6060:30;6057:2;;;-1:-1;;6093:12;6057:2;6138:59;6193:3;6184:6;6173:9;6169:22;6138:59;;;15377:2;6124:5;6120:16;6113:85;6304:49;6349:3;15594:2;6329:9;6325:22;6304:49;;;15594:2;6290:5;6286:16;6279:75;6461:49;6506:3;15082:2;6486:9;6482:22;6461:49;;;15082:2;6447:5;6443:16;6436:75;6625:48;6669:3;6591;6649:9;6645:22;6625:48;;;6591:3;6611:5;6607:16;6600:74;6789:48;6833:3;6755;6813:9;6809:22;6789:48;;;6755:3;6775:5;6771:16;6764:74;6950:48;6994:3;6916;6974:9;6970:22;6950:48;;;6916:3;6936:5;6932:16;6925:74;7112:48;7156:3;7078;7136:9;7132:22;7112:48;;;7078:3;7098:5;7094:16;7087:74;7238:3;;;7274:49;7319:3;7238;7299:9;7295:22;7274:49;;;7238:3;7258:5;7254:18;7247:77;7401:3;;;7437:49;7482:3;7401;7462:9;7458:22;7437:49;;;7401:3;7421:5;7417:18;7410:77;7566:3;;;7602:48;7646:3;7566;7626:9;7622:22;7602:48;;;7566:3;7586:5;7582:18;7575:76;7730:3;;;7766:48;7810:3;7730;7790:9;7786:22;7766:48;;;7730:3;7750:5;7746:18;7739:76;7883:3;;;7919:48;7963:3;7883;7943:9;7939:22;7919:48;;;7883:3;7903:5;7899:18;7892:76;8034:3;;;8070:63;8129:3;8034;8109:9;8105:22;8070:63;;;8034:3;8054:5;8050:18;8043:91;15660:86;;;;;;;15044:718;;;;;;15769:383;;15901:2;15889:9;15880:7;15876:23;15872:32;15869:2;;;-1:-1;;15907:12;15869:2;15965:17;15952:31;-1:-1;;;;;16003:18;15995:6;15992:30;15989:2;;;-1:-1;;16025:12;15989:2;8311:6;;16104:22;;8290:19;;;8286:32;-1:-1;8283:2;;;-1:-1;;8321:12;8283:2;8349:22;8311:6;8349:22;;;8463:68;8527:3;8503:22;8463:68;;;8445:16;8438:94;8627:49;8672:3;15901:2;8652:9;8648:22;8627:49;;;15901:2;8613:5;8609:16;8602:75;8780:57;8833:3;8747:2;8813:9;8809:22;8780:57;;;8747:2;8766:5;8762:16;8755:83;8934:2;8923:9;8919:18;8906:32;8892:46;;16003:18;8950:6;8947:30;8944:2;;;-1:-1;;8980:12;8944:2;9025:59;9080:3;9071:6;9060:9;9056:22;9025:59;;;8934:2;9011:5;9007:16;9000:85;9188:49;9233:3;9154;9213:9;9209:22;9188:49;;;9154:3;9174:5;9170:16;9163:75;9343:48;9387:3;9309;9367:9;9363:22;9343:48;;;9309:3;9329:5;9325:16;9318:74;9495:48;9539:3;9461;9519:9;9515:22;9495:48;;;9461:3;9481:5;9477:16;9470:74;9654:46;9696:3;9620;9676:9;9672:22;9654:46;;;9620:3;9640:5;9636:16;9629:72;9801:3;;;;9790:9;9786:19;9773:33;16003:18;9818:6;9815:30;9812:2;;;-1:-1;;9848:12;9812:2;9895:58;9949:3;9940:6;9929:9;9925:22;9895:58;;;9875:18;;;9868:86;;;;9879:5;15863:289;-1:-1;;;;;;15863:289;16159:241;;16263:2;16251:9;16242:7;16238:23;16234:32;16231:2;;;-1:-1;;16269:12;16231:2;16331:53;16376:7;16352:22;16331:53;;16655:263;;16770:2;16758:9;16749:7;16745:23;16741:32;16738:2;;;-1:-1;;16776:12;16738:2;-1:-1;10334:13;;16732:186;-1:-1;16732:186;19009:300;;93824:6;93819:3;93812:19;96030:6;96025:3;93861:4;93856:3;93852:14;96007:30;-1:-1;93861:4;96077:6;93856:3;96068:16;;96061:27;93861:4;92889:9;;97241:2;19295:6;97221:14;97217:28;93856:3;19264:39;;19257:46;;19111:198;;;;;;19317:327;;19452:5;93395:12;93824:6;93819:3;93812:19;19536:52;19581:6;93861:4;93856:3;93852:14;93861:4;19562:5;19558:16;19536:52;;;92889:9;97221:14;-1:-1;;97217:28;19600:39;;;;93861:4;19600:39;;19399:245;-1:-1;;19399:245;48662:253;97640:14;;;;;-1:-1;;;;;;97640:14;17323:74;;48887:2;48878:12;;48778:137;48922:1117;;18026:5;94476:13;94469:21;97541:3;97537:15;17989:3;17982:52;18485:5;93395:12;18596:52;18641:6;49353:1;49348:3;49344:11;18629:4;18622:5;18618:16;18596:52;;;93395:12;;;18660:16;;18596:52;93395:12;49353:1;18660:16;;18629:4;18618:16;;18596:52;;;93395:12;;;18660:16;;18596:52;93395:12;49353:1;18660:16;;18629:4;18618:16;;18596:52;;;97537:15;;;;-1:-1;;;;;;97537:15;;;49353:1;18660:16;;;;;;;48596:54;;;;97537:15;;;;;49789:11;;;48596:54;-1:-1;;97433:15;;-1:-1;;;;;;97433:15;49895:11;;;48225:56;50003:11;;;49250:789;-1:-1;;;;;49250:789;50046:428;;18485:5;93395:12;18596:52;18641:6;18636:3;18629:4;18622:5;18618:16;18596:52;;;93395:12;;;18660:16;;18596:52;93395:12;18660:16;18629:4;18618:16;;18596:52;;;18660:16;;50226:248;-1:-1;;;;50226:248;50481:275;;18485:5;93395:12;18596:52;18641:6;18636:3;18629:4;18622:5;18618:16;18596:52;;;18660:16;;;;;50617:139;-1:-1;;50617:139;50763:520;23501:66;23481:87;;23465:2;23587:12;;18117:37;;;;51246:12;;;50980:303;51290:868;94767:34;97329:15;;;-1:-1;;;;;;97329:15;47593:58;;97640:14;;;;-1:-1;;;;;;97640:14;51675:2;51666:12;;17323:74;93395:12;;51290:868;;18596:52;93395:12;51793;;;18629:4;18618:16;;18596:52;;;93395:12;;;18660:16;;18596:52;93395:12;51793;18660:16;;18629:4;18618:16;;18596:52;;;94476:13;;94469:21;97541:3;97537:15;51793:12;18660:16;;;;;;;17982:52;-1:-1;;52122:11;;;51566:592;-1:-1;;;;51566:592;52165:1104;-1:-1;;;;;;97537:15;;;;;;48596:54;;-1:-1;;;;;;94767:34;97329:15;;;;92794:18;52573:11;;47593:58;-1:-1;;;;;;97640:14;;;;;52683:12;;;17323:74;93395:12;;-1:-1;;97537:15;18596:52;93395:12;52794;;;18629:4;18618:16;;18596:52;;;18669:6;52577:3;18660:16;97537:15;48642:5;97541:3;97537:15;;52794:12;18660:16;;48596:54;97537:15;48642:5;97541:3;97537:15;;53014:11;18660:16;53014:11;48596:54;18485:5;93395:12;18439:52;;18596;18641:6;53120:11;18660:16;53120:11;18629:4;18622:5;18618:16;18596:52;;;18660:16;;;53120:11;18660:16;;52477:792;-1:-1;;;;;;;;;;52477:792;53276:222;-1:-1;;;;;94876:54;;;;17161:45;;53403:2;53388:18;;53374:124;53750:476;-1:-1;;;;;94876:54;;;17161:45;;94876:54;;;;54129:2;54114:18;;17161:45;54212:2;54197:18;;18117:37;;;;53949:2;53934:18;;53920:306;54233:349;-1:-1;;;;;94876:54;;;;17004:58;;54568:2;54553:18;;18117:37;54396:2;54381:18;;54367:215;54589:333;-1:-1;;;;;94876:54;;;17161:45;;94876:54;;54908:2;54893:18;;17161:45;54744:2;54729:18;;54715:207;55285:1280;-1:-1;;;;;94876:54;;;17161:45;;94876:54;;55835:2;55820:18;;17161:45;55670:3;55872:2;55857:18;;55850:48;;;55285:1280;;55912:78;55655:19;;;55976:6;55912:78;;;56038:9;56032:4;56028:20;56023:2;56012:9;56008:18;56001:48;56063:78;56136:4;56127:6;56063:78;;;-1:-1;;;;;95082:30;;;56218:3;56203:19;;47973:36;95082:30;;;56300:3;56285:19;;47973:36;-1:-1;;;95082:30;;;;56382:3;56367:19;;47973:36;56466:3;56451:19;;18117:37;56550:3;56535:19;18117:37;;;;56055:86;55641:924;-1:-1;;;;55641:924;57023:548;18117:37;;;95195:4;95184:16;;;;57391:2;57376:18;;48350:35;57474:2;57459:18;;18117:37;57557:2;57542:18;;18117:37;57230:3;57215:19;;57201:370;57578:330;;57735:2;57756:17;57749:47;57810:88;57735:2;57724:9;57720:18;57884:6;57876;57810:88;;57915:433;;58096:2;58117:17;58110:47;58171:88;58096:2;58085:9;58081:18;58245:6;58237;58171:88;;;58163:96;;95195:4;48378:5;95184:16;58334:2;58323:9;58319:18;48350:35;58067:281;;;;;;;58355:310;;58502:2;58523:17;58516:47;58577:78;58502:2;58491:9;58487:18;58641:6;58577:78;;58672:670;;58916:3;58938:17;58931:47;58992:78;58916:3;58905:9;58901:19;59056:6;58992:78;;;-1:-1;;;;;95082:30;;;59147:2;59132:18;;47973:36;95082:30;;;;59237:2;59222:18;;18915:57;59328:2;59313:18;18767:58;58984:86;58887:455;-1:-1;;58887:455;59992:416;60192:2;60206:47;;;20597:2;60177:18;;;93812:19;-1:-1;;;93852:14;;;20613:47;20679:12;;;60163:245;60415:416;60615:2;60629:47;;;20930:2;60600:18;;;93812:19;-1:-1;;;93852:14;;;20946:47;21012:12;;;60586:245;60838:416;61038:2;61052:47;;;21263:2;61023:18;;;93812:19;-1:-1;;;93852:14;;;21279:47;21345:12;;;61009:245;61261:416;61461:2;61475:47;;;21596:2;61446:18;;;93812:19;21632:34;93852:14;;;21612:55;21701:34;21687:12;;;21680:56;-1:-1;;;21756:12;;;21749:36;21804:12;;;61432:245;61684:416;61884:2;61898:47;;;22055:2;61869:18;;;93812:19;22091:34;93852:14;;;22071:55;-1:-1;;;22146:12;;;22139:27;22185:12;;;61855:245;62107:416;62307:2;62321:47;;;22436:2;62292:18;;;93812:19;-1:-1;;;93852:14;;;22452:38;22509:12;;;62278:245;62530:416;62730:2;62744:47;;;22760:2;62715:18;;;93812:19;-1:-1;;;93852:14;;;22776:43;22838:12;;;62701:245;62953:416;63153:2;63167:47;;;23089:2;63138:18;;;93812:19;23125:33;93852:14;;;23105:54;23178:12;;;63124:245;63376:416;63576:2;63590:47;;;23838:2;63561:18;;;93812:19;-1:-1;;;93852:14;;;23854:42;23915:12;;;63547:245;63799:416;63999:2;64013:47;;;24166:2;63984:18;;;93812:19;24202:34;93852:14;;;24182:55;-1:-1;;;24257:12;;;24250:31;24300:12;;;63970:245;64222:416;64422:2;64436:47;;;24551:2;64407:18;;;93812:19;24587:34;93852:14;;;24567:55;-1:-1;;;24642:12;;;24635:36;24690:12;;;64393:245;64645:416;64845:2;64859:47;;;24941:2;64830:18;;;93812:19;24977:34;93852:14;;;24957:55;-1:-1;;;25032:12;;;25025:28;25072:12;;;64816:245;65068:416;65268:2;65282:47;;;25323:2;65253:18;;;93812:19;-1:-1;;;93852:14;;;25339:38;25396:12;;;65239:245;65491:416;65691:2;65705:47;;;25647:2;65676:18;;;93812:19;-1:-1;;;93852:14;;;25663:42;25724:12;;;65662:245;65914:416;66114:2;66128:47;;;25975:2;66099:18;;;93812:19;-1:-1;;;93852:14;;;25991:50;26060:12;;;66085:245;66337:416;66537:2;66551:47;;;26311:2;66522:18;;;93812:19;-1:-1;;;93852:14;;;26327:49;26395:12;;;66508:245;66760:416;66960:2;66974:47;;;26646:2;66945:18;;;93812:19;26682:31;93852:14;;;26662:52;26733:12;;;66931:245;67183:416;67383:2;67397:47;;;26984:2;67368:18;;;93812:19;-1:-1;;;93852:14;;;27000:42;27061:12;;;67354:245;67606:416;67806:2;67820:47;;;27312:2;67791:18;;;93812:19;27348:34;93852:14;;;27328:55;-1:-1;;;27403:12;;;27396:43;27458:12;;;67777:245;68029:416;68229:2;68243:47;;;27709:2;68214:18;;;93812:19;27745:34;93852:14;;;27725:55;-1:-1;;;27800:12;;;27793:27;27839:12;;;68200:245;68452:416;68652:2;68666:47;;;28090:2;68637:18;;;93812:19;-1:-1;;;93852:14;;;28106:36;28161:12;;;68623:245;68875:416;69075:2;69089:47;;;28412:2;69060:18;;;93812:19;28448:34;93852:14;;;28428:55;-1:-1;;;28503:12;;;28496:26;28541:12;;;69046:245;69298:416;69498:2;69512:47;;;28792:2;69483:18;;;93812:19;28828:34;93852:14;;;28808:55;-1:-1;;;28883:12;;;28876:41;28936:12;;;69469:245;69721:416;69921:2;69935:47;;;29187:2;69906:18;;;93812:19;29223:34;93852:14;;;29203:55;-1:-1;;;29278:12;;;29271:26;29316:12;;;69892:245;70144:416;70344:2;70358:47;;;29567:2;70329:18;;;93812:19;29603:34;93852:14;;;29583:55;-1:-1;;;29658:12;;;29651:28;29698:12;;;70315:245;70567:416;70767:2;70781:47;;;29949:2;70752:18;;;93812:19;29985:34;93852:14;;;29965:55;-1:-1;;;30040:12;;;30033:31;30083:12;;;70738:245;70990:416;71190:2;71204:47;;;30334:2;71175:18;;;93812:19;30370:34;93852:14;;;30350:55;-1:-1;;;30425:12;;;30418:25;30462:12;;;71161:245;71413:416;71613:2;71627:47;;;30713:2;71598:18;;;93812:19;-1:-1;;;93852:14;;;30729:48;30796:12;;;71584:245;71836:416;72036:2;72050:47;;;31047:2;72021:18;;;93812:19;-1:-1;;;93852:14;;;31063:44;31126:12;;;72007:245;72259:416;72459:2;72473:47;;;31377:2;72444:18;;;93812:19;-1:-1;;;93852:14;;;31393:47;31459:12;;;72430:245;72682:416;72882:2;72896:47;;;31710:2;72867:18;;;93812:19;31746:34;93852:14;;;31726:55;-1:-1;;;31801:12;;;31794:31;31844:12;;;72853:245;73105:416;73305:2;73319:47;;;73290:18;;;93812:19;32131:34;93852:14;;;32111:55;32185:12;;;73276:245;73528:416;73728:2;73742:47;;;32436:2;73713:18;;;93812:19;-1:-1;;;93852:14;;;32452:44;32515:12;;;73699:245;73951:416;74151:2;74165:47;;;32766:2;74136:18;;;93812:19;32802:34;93852:14;;;32782:55;-1:-1;;;32857:12;;;32850:26;32895:12;;;74122:245;74374:416;74574:2;74588:47;;;33146:2;74559:18;;;93812:19;33182:33;93852:14;;;33162:54;33235:12;;;74545:245;74797:416;74997:2;75011:47;;;33486:2;74982:18;;;93812:19;-1:-1;;;93852:14;;;33502:47;33568:12;;;74968:245;75220:416;75420:2;75434:47;;;33819:2;75405:18;;;93812:19;33855:34;93852:14;;;33835:55;-1:-1;;;33910:12;;;33903:28;33950:12;;;75391:245;75643:416;75843:2;75857:47;;;34201:2;75828:18;;;93812:19;34237:34;93852:14;;;34217:55;-1:-1;;;34292:12;;;34285:27;34331:12;;;75814:245;76066:416;76266:2;76280:47;;;34582:2;76251:18;;;93812:19;-1:-1;;;93852:14;;;34598:39;34656:12;;;76237:245;76489:416;76689:2;76703:47;;;34907:2;76674:18;;;93812:19;34943:34;93852:14;;;34923:55;-1:-1;;;34998:12;;;34991:25;35035:12;;;76660:245;76912:416;77112:2;77126:47;;;35286:2;77097:18;;;93812:19;-1:-1;;;93852:14;;;35302:47;35368:12;;;77083:245;77335:416;77535:2;77549:47;;;35619:2;77520:18;;;93812:19;35655:31;93852:14;;;35635:52;35706:12;;;77506:245;77758:416;77958:2;77972:47;;;35957:2;77943:18;;;93812:19;35993:34;93852:14;;;35973:55;-1:-1;;;36048:12;;;36041:25;36085:12;;;77929:245;78181:416;78381:2;78395:47;;;36336:2;78366:18;;;93812:19;-1:-1;;;93852:14;;;36352:43;36414:12;;;78352:245;78604:416;78804:2;78818:47;;;36665:2;78789:18;;;93812:19;36701:34;93852:14;;;36681:55;-1:-1;;;36756:12;;;36749:33;36801:12;;;78775:245;79027:416;79227:2;79241:47;;;37052:2;79212:18;;;93812:19;37088:32;93852:14;;;37068:53;37140:12;;;79198:245;79450:416;79650:2;79664:47;;;37391:2;79635:18;;;93812:19;-1:-1;;;93852:14;;;37407:45;37471:12;;;79621:245;79873:416;80073:2;80087:47;;;37722:2;80058:18;;;93812:19;-1:-1;;;93852:14;;;37738:46;37803:12;;;80044:245;80296:416;80496:2;80510:47;;;38054:2;80481:18;;;93812:19;38090:34;93852:14;;;38070:55;-1:-1;;;38145:12;;;38138:33;38190:12;;;80467:245;80719:416;80919:2;80933:47;;;38441:2;80904:18;;;93812:19;38477:32;93852:14;;;38457:53;38529:12;;;80890:245;81142:416;81342:2;81356:47;;;38780:2;81327:18;;;93812:19;-1:-1;;;93852:14;;;38796:42;38857:12;;;81313:245;81565:416;81765:2;81779:47;;;39108:2;81750:18;;;93812:19;39144:34;93852:14;;;39124:55;-1:-1;;;39199:12;;;39192:25;39236:12;;;81736:245;81988:416;82188:2;82202:47;;;39487:2;82173:18;;;93812:19;39523:34;93852:14;;;39503:55;-1:-1;;;39578:12;;;39571:28;39618:12;;;82159:245;82411:416;82611:2;82625:47;;;39869:2;82596:18;;;93812:19;-1:-1;;;93852:14;;;39885:43;39947:12;;;82582:245;82834:416;83034:2;83048:47;;;40198:2;83019:18;;;93812:19;-1:-1;;;93852:14;;;40214:42;40275:12;;;83005:245;83257:416;83457:2;83471:47;;;40526:2;83442:18;;;93812:19;40562:34;93852:14;;;40542:55;-1:-1;;;40617:12;;;40610:31;40660:12;;;83428:245;83680:416;83880:2;83894:47;;;40911:2;83865:18;;;93812:19;40947:34;93852:14;;;40927:55;-1:-1;;;41002:12;;;40995:44;41058:12;;;83851:245;84103:416;84303:2;84317:47;;;41309:2;84288:18;;;93812:19;-1:-1;;;93852:14;;;41325:44;41388:12;;;84274:245;84526:416;84726:2;84740:47;;;41639:2;84711:18;;;93812:19;41675:34;93852:14;;;41655:55;-1:-1;;;41730:12;;;41723:40;41782:12;;;84697:245;84949:416;85149:2;85163:47;;;42033:2;85134:18;;;93812:19;42069:34;93852:14;;;42049:55;-1:-1;;;42124:12;;;42117:32;42168:12;;;85120:245;85372:416;85572:2;85586:47;;;42419:2;85557:18;;;93812:19;42455:33;93852:14;;;42435:54;42508:12;;;85543:245;85795:416;85995:2;86009:47;;;42759:2;85980:18;;;93812:19;-1:-1;;;93852:14;;;42775:43;42837:12;;;85966:245;86218:416;86418:2;86432:47;;;43088:2;86403:18;;;93812:19;-1:-1;;;93852:14;;;43104:40;43163:12;;;86389:245;86641:416;86841:2;86855:47;;;43414:2;86826:18;;;93812:19;-1:-1;;;93852:14;;;43430:45;43494:12;;;86812:245;87064:416;87264:2;87278:47;;;43745:2;87249:18;;;93812:19;43781:34;93852:14;;;43761:55;-1:-1;;;43836:12;;;43829:35;43883:12;;;87235:245;87487:416;87687:2;87701:47;;;44134:2;87672:18;;;93812:19;44170:34;93852:14;;;44150:55;44239:34;44225:12;;;44218:56;-1:-1;;;44294:12;;;44287:32;44338:12;;;87658:245;87910:416;88110:2;88124:47;;;44589:2;88095:18;;;93812:19;-1:-1;;;93852:14;;;44605:48;44672:12;;;88081:245;88333:416;88533:2;88547:47;;;44923:2;88518:18;;;93812:19;44959:34;93852:14;;;44939:55;-1:-1;;;45014:12;;;45007:32;45058:12;;;88504:245;88756:416;88956:2;88970:47;;;45309:2;88941:18;;;93812:19;-1:-1;;;93852:14;;;45325:36;45380:12;;;88927:245;89179:416;89379:2;89393:47;;;45631:2;89364:18;;;93812:19;-1:-1;;;93852:14;;;45647:43;45709:12;;;89350:245;89602:416;89802:2;89816:47;;;45960:2;89787:18;;;93812:19;45996:34;93852:14;;;45976:55;-1:-1;;;46051:12;;;46044:31;46094:12;;;89773:245;90025:362;;90198:2;90219:17;90212:47;46392:16;46386:23;94476:13;94469:21;90198:2;90187:9;90183:18;17853:34;92794:18;;97640:14;;;90198:2;46552:5;46548:16;46542:23;94876:54;46619:14;90187:9;46619:14;17161:45;46619:14;46708:5;46704:16;46698:23;46318:4;46741:14;90187:9;46741:14;46734:38;46787:73;46309:14;90187:9;46309:14;46841:12;46787:73;;;95195:4;46741:14;46942:5;46938:16;46932:23;95184:16;47005:14;90187:9;47005:14;48350:35;47005:14;47099:5;47095:16;47089:23;94476:13;94469:21;47160:14;90187:9;47160:14;17853:34;-1:-1;;;;;47160:14;47265:5;47261:16;47255:23;95082:30;46318:4;90187:9;47330:14;47973:36;90265:112;;;;;90169:218;;;;;90394:442;-1:-1;;;;;94756:46;;;;47455:37;;-1:-1;;;;;95082:30;;;;90739:2;90724:18;;18915:57;90822:2;90807:18;;18117:37;90576:2;90561:18;;90547:289;90843:222;18117:37;;;90970:2;90955:18;;90941:124;91072:333;18117:37;;;91391:2;91376:18;;18117:37;91227:2;91212:18;;91198:207;91412:218;-1:-1;;;;;95082:30;;;;47973:36;;91537:2;91522:18;;91508:122;91637:744;;-1:-1;;;;;95093:18;48002:5;95082:30;47980:3;47973:36;91890:3;92007:2;91996:9;91992:18;91985:48;92047:78;91890:3;91879:9;91875:19;92111:6;92047:78;;;95082:30;;;92202:2;92187:18;;47973:36;95082:30;;;;92283:2;92268:18;;47973:36;92366:3;92351:19;18117:37;-1:-1;92039:86;91861:520;-1:-1;;91861:520;92388:256;92450:2;92444:9;92476:17;;;-1:-1;;;;;92536:34;;92572:22;;;92533:62;92530:2;;;92608:1;;92598:12;92530:2;92450;92617:22;92428:216;;-1:-1;92428:216;96103:268;96168:1;96175:101;96189:6;96186:1;96183:13;96175:101;;;96256:11;;;96250:18;96237:11;;;96230:39;96211:2;96204:10;96175:101;;;96291:6;96288:1;96285:13;96282:2;;;-1:-1;;96168:1;96338:16;;96331:27;96152:219;97672:117;-1:-1;;;;;94876:54;;97731:35;;97721:2;;97780:1;;97770:12;97721:2;97715:74;

Swarm Source

ipfs://5dd7ac35c5f7c6f5ec79f4ba79633e7c75138d994328bbe33fa2efc0d21be22d

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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.