Contract 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4

Contract Overview

Balance:
0 BNB
Txn Hash
Block
From
To
Value [Txn Fee]
0x5d9ce71a1a16da000cd617703ac111c15c2ecaac1603636d332636a16b4c879467144872021-03-01 16:29:565 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.00426489
0xa2b2c49c8b5f3b4c2f6eccc92c9bea0c48cd69135473ce545c6c273ed8f94e5067144692021-03-01 16:29:026 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004274109
0xefeac365f49b878d14dbe4c5b915848dcfc3b47d053ec696c3369c67cdffc0f767144402021-03-01 16:27:357 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004274193
0xc09f63960058e0e062e269324c8afb13d5857ee6fb013c1aade9e965881d47bd67142962021-03-01 16:20:2314 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263798
0x8efb130fbaa0e5a42caf4cfd1dc50e8975c8254cd1e72412bf0d599f5a972cc267140082021-03-01 16:05:5929 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.00426405
0x7456e5eb786af22adf2488010cd5c737a7027e10313797a06c1e1bc61804265e67139022021-03-01 16:00:4134 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263798
0x978920c3e92b324f940da31f1a3400a26d2286ecf4f2a3fc7a809969a37955ae67138042021-03-01 15:55:4739 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.00426447
0x3ae95ad02a57bef7b0570aaecef823289cdb5a5de5503749173317b6af20cf8567136702021-03-01 15:49:0546 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264218
0x5954fdc894b772ae7b3e550dd1b8ca3d60dcd45036f8976719b5bc781206752567134572021-03-01 15:38:2656 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004273353
0x38e6e9b62e741395548e88c13d8214893dee60c2f4a2c9666b0a723e8e55bc7667134042021-03-01 15:35:4759 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264218
0x92551673e1452dc40cda2d674c5ebe46a8d7d860ff891546ff3e9e5d9229e03567133622021-03-01 15:33:411 hr 1 min ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264638
0x59daba60463c7b528b9e0d160172bff80f1d1c574aa52dcd4f5a203f1630d11167131532021-03-01 15:23:141 hr 12 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.00426489
0xd1b3bda9f92baf469fb2a0c641e95863b99cfde11061a06835a1c0a2ece9035a67130612021-03-01 15:18:381 hr 16 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004275537
0xe163043df789dee8ff588dded44024501a81a5e0320b6b1f419f658a2928e2b567130032021-03-01 15:15:441 hr 19 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264638
0xeb37772fe76958f4fa9f89c0fc342446cb117bd239e006561f747a12a1db17b767129372021-03-01 15:12:261 hr 22 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004274109
0xf9e7bfad1f7ea90a5cd80f5ecf38bac78e199b8ba82fbdff16d5650932ed825767128232021-03-01 15:06:441 hr 28 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264218
0x1b9e6020a3864608da9a094244cfc892809e272c1e4afaa47f20dedeb11c82c767128062021-03-01 15:05:531 hr 29 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004264218
0xc4f4d3e57ed467228554b027c5a1d951fb3d02058046886160c7a65604eae58d67125572021-03-01 14:53:261 hr 41 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004274781
0x3d56d0fcc982ab0f453926313f0420d5e6a7a9e938aa7f422e030e9f0cf85c5b67125162021-03-01 14:51:231 hr 43 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263462
0xf5e818a6dc19ad06f288ab73a601a54593f95540616e0931d6653fee1e5cddff67124522021-03-01 14:48:111 hr 47 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004273941
0x66401dc15495b4c2ec0562edf10e36b21adf93396e668e875c0cc2605f651e7667124102021-03-01 14:46:051 hr 49 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263966
0xdb866c3915062bc850bc37f1d0d79ab8abbf635fdb78158c4b5df7cb6a1ccad967123762021-03-01 14:44:231 hr 50 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263546
0x434e0b65217524a02900df45d56accc5bf4ac327c2779f58f80ede7f978e406767122992021-03-01 14:40:321 hr 54 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263798
0xb024ef8c4b88821fea346b1037bb87b545d559829ef1299e4f60a2c5b0ca9b0167122672021-03-01 14:38:561 hr 56 mins ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004263798
0xdef6722f3737403cecd49f84a72e11e30530849b466d324bbfdf51500fc2857d67121662021-03-01 14:33:532 hrs 1 min ago0xb0be98453de7e80deaefb11339a2b6deffc9ab13 IN  0x842e5f71f28a3c15f39bd2368d2e492dd5218ee40 BNB0.004274361
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xf37335b1c865ece05e5e6dd566a052f13bd22ec15a44c7fb9033f87499dcad0267114912021-03-01 14:00:082 hrs 35 mins ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x2178964eebf8a1a15e37a56f8ff41e076b1097d3da043e00590321df90361f5866130432021-02-26 3:09:193 days 13 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x60d9547dac123be50cb444cdc1bdf0b2beba54142924a5f08fdd6c732f90033066122262021-02-26 2:28:283 days 14 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x8630612daacd90a8c1b4f56c007fe94bd53e22295db4efd9751c96fe647435bb65899522021-02-25 7:54:464 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x0e3f3d53b569d18eda2bf9323153738b8cd629140 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0x3138aa37092e8ed0507e0bfa689bd7b929aff69423f27c6a56ff96fb44e44f9c65887292021-02-25 6:53:374 days 9 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x48edc7317a487acda0a96e030786875d727cd5e50 BNB
0xd5e90b169a914fa0ee53f34973af745e5f2e843d51fd8bf28eae57a4dc935dcf65497102021-02-23 22:22:405 days 18 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0xb0ce137d3d11d08c9654c4e4867b378d15e979bfa93df527556215e8c00586ac65494952021-02-23 22:11:555 days 18 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0xf3d6ebc89b9e7862eaf8dc376f12a83f1a25ea9d9a5f88637cd851cf33b4b46065163742021-02-22 18:35:526 days 21 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x0e2e986655c2989c2e2bead32344bf80e7490185f36391d22583ad25cfb4469464860592021-02-21 17:20:077 days 23 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0xab60a8c3f3390c63cc6201e318609afaa5d4eddae4d1a25cbdc57bf55dca2ff364859472021-02-21 17:14:317 days 23 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0xce8a12643233f2b1603f6f896edcf1905582a4a9cfaf12fce5af572c6224cf9463888262021-02-18 7:46:1711 days 8 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x26c44f80f2b720bd59ae0b0a578059fda33afe91263b5ba72fe3df0c758f379663865892021-02-18 5:54:2611 days 10 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0x9b3c89a866c3ed3d121aaf6c4beee92770df9c060.75 BNB
0x0a646dbf013866b397b2d498066027e001ae4b68f4c57d72deaccff969d5520463855642021-02-18 5:03:1111 days 11 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0xc821f308162fd6e65a1f8e44625714d2a9d509a00 BNB
0x0a646dbf013866b397b2d498066027e001ae4b68f4c57d72deaccff969d5520463855642021-02-18 5:03:1111 days 11 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0xc821f308162fd6e65a1f8e44625714d2a9d509a00 BNB
0x0a646dbf013866b397b2d498066027e001ae4b68f4c57d72deaccff969d5520463855642021-02-18 5:03:1111 days 11 hrs ago 0x842e5f71f28a3c15f39bd2368d2e492dd5218ee4 0xc821f308162fd6e65a1f8e44625714d2a9d509a00 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Exchange

Compiler Version
v0.6.8+commit.0bbfe453

Optimization Enabled:
Yes with 266 runs

Other Settings:
default evmVersion, GNU LGPLv3 license

Contract Source Code (Solidity Multiple files format)

File 1 of 13: 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 BNB 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 BNB 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');

    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 BNB
   */
  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 for BNB');
    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 for BNB');

    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 BNB, 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 BNB 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 - BNB 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'
    );
    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 13: Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.2;

/**
 * @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 13: 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, 'BNB'), 'BNB symbol reserved');

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

    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('BNB', symbol)) {
      return getBnbAsset();
    }

    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 BNB is modeled as an always-confirmed Asset struct for programmatic consistency
   */
  function getBnbAsset() private pure returns (Structs.Asset memory) {
    return Structs.Asset(true, address(0x0), 'BNB', 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 13: 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 BNB 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 BNB 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),
        'BNB 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 13: 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 13: ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @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 13: 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 2
    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 BNB 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 (BNB 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 13: Migrations.sol
// SPDX-License-Identifier: LGPL-3.0-only

// IGNORE This is generated by Truffle
// https://www.trufflesuite.com/docs/truffle/getting-started/running-migrations#initial-migration

pragma solidity 0.6.8;


contract Migrations {
  address public owner;
  uint256 public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function setCompleted(uint256 completed) public restricted {
    last_completed_migration = completed;
  }
}

File 9 of 13: 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 10 of 13: SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @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 11 of 13: 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 12 of 13: 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 == 2,
      'Signature hash version must be 2'
    );
    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 'BNB', the resulting market symbol is
   * 'IDEX-BNB'. 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 13 of 13: 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 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"}]

60a060405234801561001057600080fd5b5033606081901b608052600080546001600160a01b03191682178155615ff89062000049903980610f6b52806113ab5250615ff86000f3fe6080604052600436106101815760003560e01c806372e8f08d116100d1578063ae0e969e1161008a578063dbb3653511610064578063dbb3653514610441578063dcc6349014610461578063eb5068f214610481578063ef3b9d4a1461049657610181565b8063ae0e969e146103d4578063ba22bd7614610401578063d7a6aec71461042157610181565b806372e8f08d1461032a578063869af2121461034a57806390d49b9d14610377578063985c4af51461039757806398ea5fca146103b75780639a202d47146103bf57610181565b8063403f37311161013e5780634a284ef9116101185780634a284ef9146102b557806365fce5b7146102ca5780636bb509f2146102ea578063704b6c021461030a57610181565b8063403f3731146102555780634196818214610275578063457aa3c61461029557610181565b80630226b70e1461018657806302ca6002146101a85780630c187a72146101d357806313cfda2c146101e85780631abb58321461021557806320e6a8e314610235575b600080fd5b34801561019257600080fd5b506101a66101a13660046145bb565b6104b6565b005b3480156101b457600080fd5b506101bd610580565b6040516101ca9190614c75565b60405180910390f35b3480156101df57600080fd5b506101a6610590565b3480156101f457600080fd5b50610208610203366004614519565b610614565b6040516101ca9190615ea1565b34801561022157600080fd5b50610208610230366004614551565b6106a2565b34801561024157600080fd5b506101a66102503660046145a3565b610776565b34801561026157600080fd5b506101a66102703660046144fd565b610807565b34801561028157600080fd5b506101a661029036600461464b565b6108a1565b3480156102a157600080fd5b506101a66102b03660046145bb565b610923565b3480156102c157600080fd5b506101a66109d6565b3480156102d657600080fd5b506101a66102e53660046146ef565b610a4a565b3480156102f657600080fd5b506101a66103053660046148b2565b610bf1565b34801561031657600080fd5b506101a66103253660046144fd565b610f60565b34801561033657600080fd5b506101a6610345366004614551565b61101e565b34801561035657600080fd5b5061036a610365366004614694565b6110db565b6040516101ca9190615e05565b34801561038357600080fd5b506101a66103923660046144fd565b611135565b3480156103a357600080fd5b506101a66103b23660046144fd565b611206565b6101a6611392565b3480156103cb57600080fd5b506101a66113a0565b3480156103e057600080fd5b506103f46103ef3660046145a3565b6113fa565b6040516101ca9190615eb8565b34801561040d57600080fd5b506101a661041c3660046144fd565b611418565b34801561042d57600080fd5b506101a661043c3660046149c4565b6114e9565b34801561044d57600080fd5b506103f461045c366004614519565b611661565b34801561046d57600080fd5b506101a661047c366004614620565b6116be565b34801561048d57600080fd5b506101a66116f3565b3480156104a257600080fd5b506103f46104b1366004614551565b6117a5565b6000546001600160a01b031633146104e95760405162461bcd60e51b81526004016104e090615976565b60405180910390fd5b6105358484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff61184f16565b836001600160a01b03167fa8968236dc4afb2c189afeb69c91b02445e6bd09f0ffe27256b62838edc99edb84848460405161057293929190614d8a565b60405180910390a250505050565b600c546001600160a01b03165b90565b6000546001600160a01b031633146105ba5760405162461bcd60e51b81526004016104e090615976565b600b546040517fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39916105fa916001600160a01b0390911690600090614cc6565b60405180910390a1600b80546001600160a01b0319169055565b60006001600160a01b03831661063c5760405162461bcd60e51b81526004016104e090615b96565b6106446141a2565b61065560018463ffffffff611ba716565b6001600160a01b038086166000908152600660209081526040808320938816835292905220546060820151919250610698916001600160401b0390911690611d00565b9150505b92915050565b60006001600160a01b0384166106ca5760405162461bcd60e51b81526004016104e090615b96565b6106d26141a2565b61072484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506107159250611d78915050565b6001919063ffffffff611d8016565b6001600160a01b0380871660009081526006602090815260408083208286015190941683529290522054606082015191925061076b916001600160401b0390911690611d00565b9150505b9392505050565b6000546001600160a01b031633146107a05760405162461bcd60e51b81526004016104e090615976565b619d8081106107c15760405162461bcd60e51b81526004016104e0906155ec565b600a8054908290556040517f9a22227d6c0251a79ef8b846202ddcbe9d682ee5482e84abeec6dda096398a6f906107fb9083908590615eaa565b60405180910390a15050565b6000546001600160a01b031633146108315760405162461bcd60e51b81526004016104e090615976565b6005546001600160a01b03161561085a5760405162461bcd60e51b81526004016104e0906157e8565b61086381611fea565b61087f5760405162461bcd60e51b81526004016104e090614f54565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b60006108e684848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506107159250611d78915050565b6020015190506001600160a01b0381166109125760405162461bcd60e51b81526004016104e090615715565b61091d338284611ff0565b50505050565b6000546001600160a01b0316331461094d5760405162461bcd60e51b81526004016104e090615976565b6109998484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392508691505063ffffffff61221616565b836001600160a01b03167fcf66acda7673c71cd9458c59067eb30a473eeaf2fa05d008acf47616cbbbe01c84848460405161057293929190614d8a565b3360009081526009602052604090205460ff16610a055760405162461bcd60e51b81526004016104e090615b6b565b33600081815260096020526040808220805460ff19168155600101829055517fb771d4b2a83beca38f442c8903629e0e8ab1a07cf76e94eb2977153167e209369190a2565b600b546001600160a01b03163314610a745760405162461bcd60e51b81526004016104e090614e68565b610a8183604001516123c1565b15610a9e5760405162461bcd60e51b81526004016104e090615cb3565b610aab82604001516123c1565b15610ac85760405162461bcd60e51b81526004016104e090615173565b81604001516001600160a01b031683604001516001600160a01b03161415610b025760405162461bcd60e51b81526004016104e0906154fd565b610b0d8383836123f2565b610b18838383612667565b610b228383612778565b600080610b3085858561280a565b91509150610b3d83612835565b610b4a8583868487612a2f565b610b55858585612a4c565b8260200151604051610b679190614b29565b60405190819003812084519091610b7e9190614b29565b60405180910390207fbd75b2e24c3d43ec9f10f3583265606c0a7d8b67d147f9dc48376b81456823a1876040015187604001518760000151886020015189608001518a60a001518b61018001518b8b604051610be299989796959493929190614ce0565b60405180910390a35050505050565b600b546001600160a01b03163314610c1b5760405162461bcd60e51b81526004016104e090614e68565b610c2881604001516123c1565b15610c455760405162461bcd60e51b81526004016104e090615d32565b6107d06001600160401b0316610c638260c001518360a00151612d4c565b6001600160401b03161115610c8a5760405162461bcd60e51b81526004016104e09061574c565b6000610c9582612d83565b60008181526004602052604090205490915060ff1615610cc75760405162461bcd60e51b81526004016104e09061581f565b610ccf6141a2565b600083516001811115610cde57fe5b14610cff576080830151610cfa9060019063ffffffff611ba716565b610d14565b610d1483606001516107158560200151612dc1565b90506000610d3c8460c001518560a001516001600160401b0316612e5190919063ffffffff16565b90506000610d4e828460600151611d00565b60a08601516040808801516001600160a01b03908116600090815260066020908152838220898201519093168252919091529081205492935091610da3916001600160401b039091169063ffffffff612e5116565b90506000610db5828660600151611d00565b6040888101516001600160a01b039081166000908152600660208181528483208b8201805186168552908252858420805467ffffffffffffffff19166001600160401b038b81169190911790915560c08f0151600c548716865293835286852091519095168452905292902054929350610e30921690612e93565b600c546001600160a01b0390811660009081526006602090815260408083208a8301805186168552925291829020805467ffffffffffffffff19166001600160401b0395909516949094179093556005548a82015193519151636ce5768960e11b815292169263d9caed1292610eab92908890600401614c89565b600060405180830381600087803b158015610ec557600080fd5b505af1158015610ed9573d6000803e3d6000fd5b505050600087815260046020908152604091829020805460ff19166001179055870151898201518883015160a08c015193516001600160a01b03938416955091909216927f6960a1f64ecf9da0d1d1bcbfa3dd27f8c1c60de69b13faa28127dafa36c111e492610f4f9290919088908890614dc4565b60405180910390a350505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610fa85760405162461bcd60e51b81526004016104e0906157ba565b6001600160a01b038116610fce5760405162461bcd60e51b81526004016104e090615b96565b6000546001600160a01b0382811691161415610ffc5760405162461bcd60e51b81526004016104e0906150a2565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031633146110485760405162461bcd60e51b81526004016104e090615976565b6110938383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001949392505063ffffffff612ec4169050565b826001600160a01b03167f7f63a63f4a2ea318da0b031794d0b5a3183a1b2de54e053ceb17cabdba03173583836040516110ce929190614d76565b60405180910390a2505050565b6110e36141a2565b61112d84848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019392508691505063ffffffff611d8016565b949350505050565b6000546001600160a01b0316331461115f5760405162461bcd60e51b81526004016104e090615976565b6001600160a01b0381166111855760405162461bcd60e51b81526004016104e090615b96565b600c546001600160a01b03828116911614156111b35760405162461bcd60e51b81526004016104e090615d87565b600c80546001600160a01b038381166001600160a01b03198316179092556040519116907f9f4f5dce3c4d197b5d7496cb96e25f0a89809167195964b0daa3ef5fed63c00a906107fb9083908590614cc6565b61120f336123c1565b61122b5760405162461bcd60e51b81526004016104e090615462565b6112336141a2565b61124460018363ffffffff611ba716565b3360009081526006602090815260408083206001600160a01b038716845290915281205460608301519293506001600160401b031691611285908390611d00565b9050600081116112a75760405162461bcd60e51b81526004016104e090615d59565b3360008181526006602090815260408083206001600160a01b038981168552925291829020805467ffffffffffffffff191690556005549151636ce5768960e11b815291169163d9caed1291611304919088908690600401614c89565b600060405180830381600087803b15801561131e57600080fd5b505af1158015611332573d6000803e3d6000fd5b50505050836001600160a01b0316336001600160a01b03167f02e03f232c88c87047c5a9cfbad1213b842503fee6a002ec2eec9f5a64725bf48560400151856000806040516113849493929190614dc4565b60405180910390a350505050565b61139e33600034611ff0565b565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113e85760405162461bcd60e51b81526004016104e0906157ba565b600080546001600160a01b0319169055565b6000818152600860205260409020546001600160401b03165b919050565b6000546001600160a01b031633146114425760405162461bcd60e51b81526004016104e090615976565b6001600160a01b0381166114685760405162461bcd60e51b81526004016104e090615b96565b600b546001600160a01b03828116911614156114965760405162461bcd60e51b81526004016104e090615d87565b600b80546001600160a01b038381166001600160a01b03198316179092556040519116907fddb86df2d5262dd7b44067db5962cc7875e9db409cb21c88adfe3c5760315e39906107fb9083908590614cc6565b60006114f482612dc1565b90506114fe6130d9565b6001600160401b0316816001600160401b03161061152e5760405162461bcd60e51b81526004016104e090615421565b3360009081526007602052604090205460ff16156115b757336000908152600760205260409020546001600160401b0380831661010090920416106115855760405162461bcd60e51b81526004016104e090614f11565b336000908152600760205260409020600101544310156115b75760405162461bcd60e51b81526004016104e090615a6e565b600a546040805160608101825260018082526001600160401b0385811660208085019182524390960184860181815233600081815260079099529787902095518654935160ff199094169015151768ffffffffffffffff001916610100939094169290920292909217845551929091019190915590519091907f10cf19671b43c88b1f02d4e94932d7ffaa89c7278bc5b8868fa7b7676210809b906110ce90869086908690615e77565b60006001600160a01b0383166116895760405162461bcd60e51b81526004016104e090615b96565b506001600160a01b0391821660009081526006602090815260408083209390941682529190915220546001600160401b031690565b6001600160a01b0382166116e45760405162461bcd60e51b81526004016104e090615715565b6116ef338383611ff0565b5050565b3360009081526009602052604090205460ff16156117235760405162461bcd60e51b81526004016104e09061557b565b6040805180820182526001808252600a80544390810160208086019182523360008181526009909252908790209551865460ff19169015151786559051949093019390935554925190927fd60f9f7b2f1a208268475a927bd727c4e198fc8b40aab3004ebcc2bc78ca84809261179b92910190615ea1565b60405180910390a2565b60006001600160a01b0384166117cd5760405162461bcd60e51b81526004016104e090615b96565b600061181284848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506107159250611d78915050565b6020908101516001600160a01b03808816600090815260068452604080822092909316815292529020546001600160401b03169150509392505050565b6118576141a2565b6001600160a01b0384811660009081526020878152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f810185900485028701850186528087529195929486019390919083018282801561191a5780601f106118ef5761010080835404028352916020019161191a565b820191906000526020600020905b8154815290600101906020018083116118fd57829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091506119765760405162461bcd60e51b81526004016104e09061526d565b8060800151156119985760405162461bcd60e51b81526004016104e090615856565b6119a68160400151846130e6565b6119c25760405162461bcd60e51b81526004016104e090614f7d565b8160ff16816060015160ff16146119eb5760405162461bcd60e51b81526004016104e0906154ce565b6001608082018190526001600160401b036103e842021660a08301526001600160a01b0380861660009081526020888152604091829020855181548388015190951661010002610100600160a81b031991151560ff1990961695909517169390931783559084015180518594611a66939085019201906141d8565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000269ffffffffffffffff0000199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790556040516001860190611ad1908590614b29565b9081526040805191829003602090810190922080546001818101835560009283529184902085516003909202018054868601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559184015180518594611b489385019201906141d8565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000269ffffffffffffffff0000199415156101000261ff001960ff90951660ff19909316929092179390931617929092161790555050505050565b611baf6141a2565b6001600160a01b038216611bcc57611bc561313f565b905061069c565b611bd46141a2565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015611c975780601f10611c6c57610100808354040283529160200191611c97565b820191906000526020600020905b815481529060010190602001808311611c7a57829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015611ce4575080608001515b61076f5760405162461bcd60e51b81526004016104e090615932565b600060208260ff161115611d265760405162461bcd60e51b81526004016104e0906159a4565b60088260ff161115611d5557611bc56001600160401b03841660ff600719850116600a0a63ffffffff61319816565b61076f6001600160401b03841660ff600885900316600a0a63ffffffff6131d216565b426103e80290565b611d886141a2565b611dad6040518060400160405280600381526020016221272160e91b815250846130e6565b15611dc157611dba61313f565b905061076f565b611dc96141a2565b60008560010185604051611ddd9190614b29565b908152604051908190036020019020541115611fbf5760005b8560010185604051611e089190614b29565b9081526040519081900360200190205460ff82161015611fbd57836001600160401b03168660010186604051611e3e9190614b29565b90815260200160405180910390208260ff1681548110611e5a57fe5b60009182526020909120600390910201600201546201000090046001600160401b031611611fb5578560010185604051611e949190614b29565b90815260200160405180910390208160ff1681548110611eb057fe5b60009182526020918290206040805160c081018252600393909302909101805460ff811615158452610100908190046001600160a01b031684860152600180830180548551600293821615909402600019011691909104601f8101879004870283018701855280835294959294938601939192909190830182828015611f775780601f10611f4c57610100808354040283529160200191611f77565b820191906000526020600020905b815481529060010190602001808311611f5a57829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015291505b600101611df6565b505b80518015611fce575080608001515b61112d5760405162461bcd60e51b81526004016104e090615667565b3b151590565b6001600160a01b03831660009081526009602052604090205460ff16156120295760405162461bcd60e51b81526004016104e090615d32565b6120316141a2565b61204260018463ffffffff611ba716565b90506000612054838360600151613214565b90506000816001600160401b03161161207f5760405162461bcd60e51b81526004016104e0906151aa565b600061208f828460600151611d00565b90506001600160a01b038516156120ab576120ab8686836132a9565b6005546120c2906001600160a01b0316868361343c565b6001600160a01b038087166000908152600660209081526040808320938916835292905290812054612103906001600160401b03168463ffffffff612e9316565b90506000612115828660600151611d00565b6001600160a01b038981166000908152600660209081526040808320938c168352929052819020805467ffffffffffffffff19166001600160401b03868116919091179091556005805467ffffffffffffffff60a01b198116600160a01b9182900484166001019093160291909117905586810151905191925061219891614b29565b6040518091039020876001600160a01b0316896001600160a01b03167fc2813a84168de59cf710030ba2acb13031b567b8ba76f70e8ab0782aa9469a7a600560149054906101000a90046001600160401b03168960400151898888604051612204959493929190615ecc565b60405180910390a45050505050505050565b60208160ff16111561223a5760405162461bcd60e51b81526004016104e0906153ad565b6001600160a01b03831615801590612256575061225683611fea565b6122725760405162461bcd60e51b81526004016104e090615a3f565b60008251116122935760405162461bcd60e51b81526004016104e090615b3d565b6001600160a01b038316600090815260208590526040902060020154610100900460ff16156122d45760405162461bcd60e51b81526004016104e090615856565b6040805160c08101825260018082526001600160a01b03868116602080850182815285870189815260ff8916606088015260006080880181905260a088018190529384528b83529690922085518154935160ff1990941690151517610100600160a81b0319166101009390941692909202929092178155935180519394936123639385019291909101906141d8565b50606082015160029091018054608084015160a0909401516001600160401b0316620100000269ffffffffffffffff0000199415156101000261ff001960ff90951660ff199093169290921793909316179290921617905550505050565b6001600160a01b0381166000908152600960205260408120805460ff16801561076f57506001015443101592915050565b80606001516001600160a01b031681604001516001600160a01b0316141561242c5760405162461bcd60e51b81526004016104e090615534565b6124346141a2565b61244982600001516107158660200151612dc1565b90506124536141a2565b61246883602001516107158760200151612dc1565b905082604001516001600160a01b031682602001516001600160a01b03161480156124ac575082606001516001600160a01b031681602001516001600160a01b0316145b6124c85760405162461bcd60e51b81526004016104e0906151d7565b6124d06141a2565b6124e584600001516107158760200151612dc1565b90506124ef6141a2565b61250485602001516107158860200151612dc1565b905084604001516001600160a01b031682602001516001600160a01b0316148015612548575084606001516001600160a01b031681602001516001600160a01b0316145b6125645760405162461bcd60e51b81526004016104e0906159eb565b84604001516001600160a01b03168561010001516001600160a01b031614806125a7575084606001516001600160a01b03168561010001516001600160a01b0316145b6125c35760405162461bcd60e51b81526004016104e090615369565b84604001516001600160a01b03168561012001516001600160a01b03161480612606575084606001516001600160a01b03168561012001516001600160a01b0316145b6126225760405162461bcd60e51b81526004016104e090615623565b8461012001516001600160a01b03168561010001516001600160a01b0316141561265e5760405162461bcd60e51b81526004016104e090615056565b50505050505050565b600081608001516001600160401b0316116126945760405162461bcd60e51b81526004016104e090615dbe565b60008160a001516001600160401b0316116126c15760405162461bcd60e51b81526004016104e090615cea565b6126ce8360600151613621565b1561271a578060a001516001600160401b03166126f382608001518560e0015161367a565b6001600160401b0316101561271a5760405162461bcd60e51b81526004016104e09061588d565b6127278260600151613621565b15612773578060a001516001600160401b031661274c82608001518460e0015161367a565b6001600160401b031611156127735760405162461bcd60e51b81526004016104e090615b06565b505050565b61278582604001516136d3565b6001600160401b031661279b8360200151612dc1565b6001600160401b0316116127c15760405162461bcd60e51b81526004016104e0906158f1565b6127ce81604001516136d3565b6001600160401b03166127e48260200151612dc1565b6001600160401b0316116116ef5760405162461bcd60e51b81526004016104e090615294565b60008060006128198685613750565b905060006128278686613750565b919791965090945050505050565b600081604001516001600160a01b03168261010001516001600160a01b031614612863578160a00151612869565b81608001515b90506107d06001600160401b031661288683610140015183612d4c565b6001600160401b031611156128ad5760405162461bcd60e51b81526004016104e09061510f565b600082604001516001600160a01b03168361012001516001600160a01b0316146128db578260a001516128e1565b82608001515b90506107d06001600160401b03166128fe84610160015183612d4c565b6001600160401b031611156129255760405162461bcd60e51b81526004016104e090614fe2565b82608001516001600160401b031661298484604001516001600160a01b03168561010001516001600160a01b0316146129635784610160015161296a565b8461014001515b60c08601516001600160401b03169063ffffffff612e9316565b6001600160401b0316146129aa5760405162461bcd60e51b81526004016104e09061500f565b8260a001516001600160401b0316612a0984606001516001600160a01b03168561010001516001600160a01b0316146129e8578461016001516129ef565b8461014001515b60e08601516001600160401b03169063ffffffff612e9316565b6001600160401b0316146127735760405162461bcd60e51b81526004016104e090615abe565b612a3a8585836137f1565b612a458383836137f1565b5050505050565b60808101516040808401516001600160a01b03908116600090815260066020908152838220848701519093168252919091522054612a9b916001600160401b039091169063ffffffff612e5116565b6040838101516001600160a01b03908116600090815260066020818152848320878601805186168552908252858420805467ffffffffffffffff19166001600160401b0398891617905560c08801518a8701518616855292825285842090519094168352929092529190912054612b13921690612e93565b604084810180516001600160a01b0390811660009081526006602081815285832088870151851684528152858320805467ffffffffffffffff19166001600160401b0398891617905560a088015194518416835290815284822060608801519093168252919091529190912054612b8b921690612e51565b6040848101516001600160a01b0390811660009081526006602081815284832060608801805186168552908252858420805467ffffffffffffffff19166001600160401b0398891617905560e0880151898701518616855292825285842090519094168352929092529190912054612c04921690612e93565b6040838101516001600160a01b039081166000908152600660208181528483206060880151851684528152848320805467ffffffffffffffff19166001600160401b03978816179055610140870151600c54851684529181528483206101008801519094168352929092529190912054612c7f921690612e93565b600c80546001600160a01b039081166000908152600660208181526040808420610100890151861685528252808420805467ffffffffffffffff19166001600160401b039889161790556101608801519554851684529181528183206101208801519094168352929092522054612cf7921690612e93565b600c546001600160a01b0390811660009081526006602090815260408083206101209690960151909316825293909352909120805467ffffffffffffffff19166001600160401b039092169190911790555050565b600061271061069883612d6e6001600160401b0387168463ffffffff61397716565b6001600160401b03169063ffffffff6139d216565b600080612d8f83613a14565b9050612da5818461010001518560400151613acf565b61069c5760405162461bcd60e51b81526004016104e090614e31565b6000600f604c83901c1660018114612deb5760405162461bcd60e51b81526004016104e0906150e6565b670fff000000000000601084901c1665ffff00000000603085901c1663ffffffff606086901c168183178117612e46650b1d069b54006127106001600160801b038416046001600160401b0316612e5190919063ffffffff16565b979650505050505050565b600061076f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613aff565b60008282016001600160401b03808516908216101561076f5760405162461bcd60e51b81526004016104e09061513c565b612ecc6141a2565b6001600160a01b0383811660009081526020868152604091829020825160c081018452815460ff8116151582526101009081900490951681840152600180830180548651600293821615909802600019011691909104601f8101859004850287018501865280875291959294860193909190830182828015612f8f5780601f10612f6457610100808354040283529160200191612f8f565b820191906000526020600020905b815481529060010190602001808311612f7257829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015280519091508015612fdc575080608001515b612ff85760405162461bcd60e51b81526004016104e09061522a565b61301d826040518060400160405280600381526020016221272160e91b8152506130e6565b1561303a5760405162461bcd60e51b81526004016104e090615c86565b426103e89081026001600160401b031660a08301526040516001860190613062908590614b29565b9081526040805191829003602090810190922080546001818101835560009283529184902086516003909202018054878601516001600160a01b031661010002610100600160a81b031993151560ff1990921691909117929092169190911781559185015180518694611b489385019201906141d8565b4262015180016103e80290565b6000816040516020016130f99190614b29565b60405160208183030381529060405280519060200120836040516020016131209190614b29565b6040516020818303038152906040528051906020012014905092915050565b6131476141a2565b506040805160c08101825260018082526000602080840182905284518086018652600381526221272160e91b918101919091529383019390935260126060830152608082015260a081019190915290565b6000826131a75750600061069c565b828202828482816131b457fe5b041461076f5760405162461bcd60e51b81526004016104e0906156d4565b600061076f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613b3e565b600060208260ff16111561323a5760405162461bcd60e51b81526004016104e0906159a4565b600060088360ff161115613269576132628460ff600719860116600a0a63ffffffff6131d216565b9050613286565b6132838460ff600886900316600a0a63ffffffff61319816565b90505b600160401b811061076f5760405162461bcd60e51b81526004016104e090615783565b6040516370a0823160e01b81526000906001600160a01b038416906370a08231906132d8903090600401614c75565b60206040518083038186803b1580156132f057600080fd5b505afa158015613304573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061332891906149df565b6040516323b872dd60e01b81529091506001600160a01b038416906323b872dd9061335b90879030908790600401614c89565b600060405180830381600087803b15801561337557600080fd5b505af1158015613389573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a08231906133bc903090600401614c75565b60206040518083038186803b1580156133d457600080fd5b505afa1580156133e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061340c91906149df565b90508261341f828463ffffffff613b7516565b14612a455760405162461bcd60e51b81526004016104e090614e9f565b6001600160a01b038216613490576040516001600160a01b0384169082156108fc029083906000818181858888f1935050505061348b5760405162461bcd60e51b81526004016104e0906153f4565b612773565b6040516370a0823160e01b81526000906001600160a01b038416906370a08231906134bf908790600401614c75565b60206040518083038186803b1580156134d757600080fd5b505afa1580156134eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350f91906149df565b60405163a9059cbb60e01b81529091506001600160a01b0384169063a9059cbb906135409087908690600401614cad565b600060405180830381600087803b15801561355a57600080fd5b505af115801561356e573d6000803e3d6000fd5b50506040516370a0823160e01b8152600092506001600160a01b03861691506370a08231906135a1908890600401614c75565b60206040518083038186803b1580156135b957600080fd5b505afa1580156135cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f191906149df565b905082613604828463ffffffff613b7516565b14612a455760405162461bcd60e51b81526004016104e090615c18565b6000600182600681111561363157fe5b14806136485750600282600681111561364657fe5b145b8061365e5750600482600681111561365c57fe5b145b8061069c575060065b82600681111561367357fe5b1492915050565b60006305f5e100816136ae826136a26001600160401b0388811690881663ffffffff61319816565b9063ffffffff6131d216565b9050600160401b81106106985760405162461bcd60e51b81526004016104e090615bcd565b6001600160a01b03811660009081526007602052604081205460ff16801561371657506001600160a01b0382166000908152600760205260409020600101544310155b1561374857506001600160a01b03811660009081526007602052604090205461010090046001600160401b0316611413565b506000919050565b6000806137668484600001518560200151613bb7565b905061377c81856101a001518660400151613acf565b60008560800151600181111561378e57fe5b146137b157604051806060016040528060278152602001615f9c602791396137cb565b604051806060016040528060268152602001615f76602691395b906137e95760405162461bcd60e51b81526004016104e09190614db1565b509392505050565b60008281526003602052604090205460ff16156138205760405162461bcd60e51b81526004016104e0906158c4565b60008360c001511561388c576138398460600151613d59565b6138555760405162461bcd60e51b81526004016104e0906152d6565b60008381526008602052604090205460a0830151613885916001600160401b03918216911663ffffffff612e9316565b90506138bf565b60008381526008602052604090205460808301516138bc916001600160401b03918216911663ffffffff612e9316565b90505b8360a001516001600160401b0316816001600160401b031611156138f55760405162461bcd60e51b81526004016104e0906156aa565b8360a001516001600160401b0316816001600160401b03161015613940576000838152600860205260409020805467ffffffffffffffff19166001600160401b03831617905561091d565b50506000908152600860209081526040808320805467ffffffffffffffff1916905560039091529020805460ff1916600117905550565b60006001600160401b03831661398f5750600061069c565b8282026001600160401b0380841690808616908316816139ab57fe5b046001600160401b03161461076f5760405162461bcd60e51b81526004016104e0906156d4565b600061076f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613d8c565b60208101516040820151600091908284516001811115613a3057fe5b14613a5e578360800151604051602001613a4a9190614a4d565b604051602081830303815290604052613a83565b8360600151604051602001613a739190614b29565b6040516020818303038152906040525b613a998560a001516001600160401b0316613dd4565b8560e00151604051602001613ab2959493929190614b76565b604051602081830303815290604052805190602001209050919050565b6000816001600160a01b0316613aed613ae786613ed0565b85613ee3565b6001600160a01b031614949350505050565b6000836001600160401b0316836001600160401b031611158290613b365760405162461bcd60e51b81526004016104e09190614db1565b505050900390565b60008183613b5f5760405162461bcd60e51b81526004016104e09190614db1565b506000838581613b6b57fe5b0495945050505050565b600061076f83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614011565b6000836000015160ff16600214613be05760405162461bcd60e51b81526004016104e090615499565b835160208501516040860151613bf68686614035565b87606001516006811115613c0657fe5b88608001516001811115613c1657fe5b613c2c8a60a001516001600160401b0316613dd4565b604051602001613c429796959493929190614be6565b6040516020818303038152906040528460c0015160008660e001516001600160401b031611613c805760405180602001604052806000815250613c96565b613c968660e001516001600160401b0316613dd4565b60008761010001516001600160401b031611613cc15760405180602001604052806000815250613cd8565b613cd88761010001516001600160401b0316613dd4565b8761012001518861014001516003811115613cef57fe5b8961016001516003811115613d0057fe5b8a6101800151604051602001613d1c9796959493929190614a6a565b60408051601f1981840301815290829052613d3a9291602001614afb565b6040516020818303038152906040528051906020012090509392505050565b600080826006811115613d6857fe5b1480613d7f57506003826006811115613d7d57fe5b145b8061069c57506005613667565b6000816001600160401b038416613db65760405162461bcd60e51b81526004016104e09190614db1565b506000836001600160401b0316856001600160401b031681613b6b57fe5b60608160005b8115613dee57600101600a82049150613dda565b6009811015613dfb575060095b6001016060816001600160401b0381118015613e1657600080fd5b506040519080825280601f01601f191660200182016040528015613e41576020820181803683370190505b509050815b8015613ec75780830360081415613e8757602e60f81b826001830381518110613e6b57fe5b60200101906001600160f81b031916908160001a905350613ebe565b600a860660300160f81b826001830381518110613ea057fe5b60200101906001600160f81b031916908160001a905350600a860495505b60001901613e46565b50949350505050565b600081604051602001613ab29190614b45565b60008151604114613f065760405162461bcd60e51b81526004016104e090614fab565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115613f585760405162461bcd60e51b81526004016104e090615327565b8060ff16601b14158015613f7057508060ff16601c14155b15613f8d5760405162461bcd60e51b81526004016104e0906155aa565b600060018783868660405160008152602001604052604051613fb29493929190614d58565b6020604051602081039080840390855afa158015613fd4573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166140075760405162461bcd60e51b81526004016104e090614dfa565b9695505050505050565b60008184841115613b365760405162461bcd60e51b81526004016104e09190614db1565b604080518082019091526001808252602d60f81b60208301528251845160609386939092869286929101016001600160401b038111801561407557600080fd5b506040519080825280601f01601f1916602001820160405280156140a0576020820181803683370190505b5090506000805b85518210156140fb578582815181106140bc57fe5b602001015160f81c60f81b8382806001019350815181106140d957fe5b60200101906001600160f81b031916908160001a9053506001909101906140a7565b8460008151811061410857fe5b602001015160f81c60f81b83828060010193508151811061412557fe5b60200101906001600160f81b031916908160001a905350600091505b83518210156141955783828151811061415657fe5b602001015160f81c60f81b83828060010193508151811061417357fe5b60200101906001600160f81b031916908160001a905350600190910190614141565b5090979650505050505050565b6040805160c08101825260008082526020820181905260609282018390529181018290526080810182905260a081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061421957805160ff1916838001178555614246565b82800160010185558215614246579182015b8281111561424657825182559160200191906001019061422b565b50614252929150614256565b5090565b61058d91905b80821115614252576000815560010161425c565b803561069c81615f5d565b8035801515811461069c57600080fd5b600082601f83011261429b578081fd5b81356001600160401b038111156142b0578182fd5b6142c3601f8201601f1916602001615f0b565b91508082528360208285010111156142da57600080fd5b8060208401602084013760009082016020015292915050565b80356004811061069c57600080fd5b80356002811061069c57600080fd5b80356007811061069c57600080fd5b60008083601f840112614331578182fd5b5081356001600160401b03811115614347578182fd5b60208301915083602082850101111561435f57600080fd5b9250929050565b60006101c0808385031215614379578182fd5b61438281615f0b565b91505061438f83836144ec565b815261439e83602084016144be565b60208201526143b08360408401614270565b60408201526143c28360608401614311565b60608201526143d48360808401614302565b60808201526143e68360a084016144d5565b60a08201526143f88360c0840161427b565b60c082015261440a8360e084016144d5565b60e082015261010061441e848285016144d5565b90820152610120828101356001600160401b038082111561443e57600080fd5b61444a8683870161428b565b83850152610140925061445f868487016142f3565b838501526101609250614474868487016142f3565b838501526101809250614489868487016144d5565b838501526101a09250828501359150808211156144a557600080fd5b506144b28582860161428b565b82840152505092915050565b80356001600160801b038116811461069c57600080fd5b80356001600160401b038116811461069c57600080fd5b803560ff8116811461069c57600080fd5b60006020828403121561450e578081fd5b813561076f81615f5d565b6000806040838503121561452b578081fd5b823561453681615f5d565b9150602083013561454681615f5d565b809150509250929050565b600080600060408486031215614565578081fd5b833561457081615f5d565b925060208401356001600160401b0381111561458a578182fd5b61459686828701614320565b9497909650939450505050565b6000602082840312156145b4578081fd5b5035919050565b600080600080606085870312156145d0578182fd5b84356145db81615f5d565b935060208501356001600160401b038111156145f5578283fd5b61460187828801614320565b9094509250614615905086604087016144ec565b905092959194509250565b60008060408385031215614632578182fd5b823561463d81615f5d565b946020939093013593505050565b60008060006040848603121561465f578081fd5b83356001600160401b03811115614674578182fd5b61468086828701614320565b909790965060209590950135949350505050565b6000806000604084860312156146a8578081fd5b83356001600160401b03808211156146be578283fd5b6146ca87838801614320565b90955093506020860135915080821682146146e3578283fd5b50809150509250925092565b600080600060608486031215614703578081fd5b83356001600160401b0380821115614719578283fd5b61472587838801614366565b9450602086013591508082111561473a578283fd5b61474687838801614366565b9350604086013591508082111561475b578283fd5b6101c091860180880383131561476f578384fd5b61477883615f0b565b8135935082841115614788578485fd5b6147948985840161428b565b815260208201359350828411156147a9578485fd5b6147b58985840161428b565b60208201526147c78960408401614270565b60408201526147d98960608401614270565b60608201526147eb89608084016144d5565b60808201526147fd8960a084016144d5565b60a082015261480f8960c084016144d5565b60c08201526148218960e084016144d5565b60e0820152610100935061483789858401614270565b84820152610120935061484c89858401614270565b848201526101409350614861898584016144d5565b848201526101609350614876898584016144d5565b84820152610180935061488b898584016144d5565b848201526101a093506148a089858401614302565b84820152809450505050509250925092565b6000602082840312156148c3578081fd5b81356001600160401b03808211156148d9578283fd5b6101209184018086038313156148ed578384fd5b6148f683615f0b565b6149008783614302565b815261490f87602084016144be565b60208201526149218760408401614270565b60408201526060820135935082841115614939578485fd5b6149458785840161428b565b60608201526149578760808401614270565b60808201526149698760a084016144d5565b60a082015261497b8760c084016144d5565b60c082015261498d8760e0840161427b565b60e0820152610100935083820135838111156149a7578586fd5b6149b38882850161428b565b948201949094529695505050505050565b6000602082840312156149d5578081fd5b61076f83836144be565b6000602082840312156149f0578081fd5b5051919050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b60008151808452614a39816020860160208601615f31565b601f01601f19169290920160200192915050565b60609190911b6bffffffffffffffffffffffff1916815260140190565b600088151560f81b82528751614a87816001850160208c01615f31565b8751908301614a9d826001830160208c01615f31565b8751918101614ab3836001830160208c01615f31565b60f897881b6001600160f81b0319908116600192909401918201939093529590961b166002850152505060c01b6001600160c01b0319166003820152600b0195945050505050565b60008351614b0d818460208801615f31565b8351908301614b20828260208801615f31565b01949350505050565b60008251614b3b818460208701615f31565b9190910192915050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b60006001600160801b03198760801b1682526bffffffffffffffffffffffff198660601b1660108301528451614bb3816024850160208901615f31565b8451908301614bc9826024830160208901615f31565b93151560f81b602494909101938401525050602501949350505050565b600060ff60f81b808a60f81b1683526001600160801b03198960801b1660018401526bffffffffffffffffffffffff198860601b1660118401528651614c33816025860160208b01615f31565b808401828860f81b166025820152828760f81b16602682015285519250614c61836027830160208901615f31565b9091016027019a9950505050505050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b60006101206001600160a01b03808d168452808c16602085015250806040840152614d0d8184018b614a21565b8381036060850152614d1f818b614a21565b6001600160401b03998a16608086015297891660a08501525050509290941660c083015260e08201526101000191909152949350505050565b93845260ff9290921660208401526040830152606082015260800190565b60006020825261112d6020830184866149f7565b600060408252614d9e6040830185876149f7565b905060ff83166020830152949350505050565b60006020825261076f6020830184614a21565b600060808252614dd76080830187614a21565b6001600160401b0395861660208401529390941660408201526060015292915050565b60208082526018908201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604082015260600190565b60208082526018908201527f496e76616c69642077616c6c6574207369676e61747572650000000000000000604082015260600190565b60208082526018908201527f43616c6c6572206973206e6f7420646973706174636865720000000000000000604082015260600190565b6020808252604c908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f46726f6d207375636365737320776974686f757420657870656374656420626160608201526b6c616e6365206368616e676560a01b608082015260a00190565b60208082526023908201527f4e6f6e63652074696d657374616d7020616c726561647920696e76616c6964616040820152621d195960ea1b606082015260800190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252601490820152730a6f2dac4ded8e640c8de40dcdee840dac2e8c6d60631b604082015260600190565b6020808252601f908201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604082015260600190565b6020808252601390820152724578636573736976652074616b65722066656560681b604082015260600190565b60208082526027908201527f4e6574206261736520706c757320666565206973206e6f7420657175616c20746040820152666f2067726f737360c81b606082015260800190565b6020808252602c908201527f4d616b657220616e642074616b65722066656520617373657473206d7573742060408201526b189948191a5999995c995b9d60a21b606082015260800190565b60208082526024908201527f4d75737420626520646966666572656e742066726f6d2063757272656e7420616040820152633236b4b760e11b606082015260800190565b6020808252600f908201526e135d5cdd081899481d8c4815555251608a1b604082015260600190565b602080825260139082015272457863657373697665206d616b65722066656560681b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601a908201527f53656c6c2077616c6c657420657869742066696e616c697a6564000000000000604082015260600190565b6020808252601390820152725175616e7469747920697320746f6f206c6f7760681b604082015260600190565b60208082526033908201527f427579206f72646572206d61726b65742073796d626f6c2061646472657373206040820152720e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d606b1b606082015260800190565b60208082526023908201527f526567697374726174696f6e206f6620746f6b656e206e6f742066696e616c696040820152621e995960ea1b606082015260800190565b6020808252600d908201526c2ab735b737bbb7103a37b5b2b760991b604082015260600190565b60208082526022908201527f53656c6c206f72646572206e6f6e63652074696d657374616d7020746f6f206c6040820152616f7760f01b606082015260800190565b60208082526031908201527f4f726465722071756f7465207175616e74697479206f6e6c792076616c696420604082015270666f72206d61726b6574206f726465727360781b606082015260800190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604082015261756560f01b606082015260800190565b60208082526024908201527f4d616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526027908201527f546f6b656e2063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b602080825260139082015272109390881d1c985b9cd9995c8819985a5b1959606a1b604082015260600190565b60208082526021908201527f4e6f6e63652074696d657374616d7020746f6f2066617220696e2066757475726040820152606560f81b606082015260800190565b60208082526019908201527f57616c6c65742065786974206e6f742066696e616c697a656400000000000000604082015260600190565b6020808252818101527f5369676e617475726520686173682076657273696f6e206d7573742062652032604082015260600190565b602080825260159082015274088cac6d2dac2d8e640c8de40dcdee840dac2e8c6d605b1b604082015260600190565b60208082526018908201527f53656c662d74726164696e67206e6f7420616c6c6f7765640000000000000000604082015260600190565b60208082526027908201527f4261736520616e642071756f746520617373657473206d7573742062652064696040820152661999995c995b9d60ca1b606082015260800190565b60208082526015908201527415d85b1b195d08185b1c9958591e48195e1a5d1959605a1b604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604082015261756560f01b606082015260800190565b60208082526018908201527f4d757374206265206c657373207468616e2031207765656b0000000000000000604082015260600190565b60208082526024908201527f54616b657220666565206173736574206973206e6f7420696e207472616465206040820152633830b4b960e11b606082015260800190565b60208082526023908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f722073796d604082015262189bdb60ea1b606082015260800190565b60208082526010908201526f13dc99195c881bdd995c999a5b1b195960821b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526018908201527f557365206465706f736974457468657220666f7220424e420000000000000000604082015260600190565b60208082526018908201527f457863657373697665207769746864726177616c206665650000000000000000604082015260600190565b6020808252601d908201527f506970207175616e74697479206f766572666c6f77732075696e743634000000604082015260600190565b60208082526014908201527321b0b63632b91036bab9ba1031329037bbb732b960611b604082015260600190565b6020808252601e908201527f437573746f6469616e2063616e206f6e6c7920626520736574206f6e63650000604082015260600190565b60208082526016908201527f4861736820616c72656164792077697468647261776e00000000000000000000604082015260600190565b60208082526017908201527f546f6b656e20616c72656164792066696e616c697a6564000000000000000000604082015260600190565b6020808252601e908201527f427579206f72646572206c696d69742070726963652065786365656465640000604082015260600190565b60208082526013908201527213dc99195c88191bdd589b1948199a5b1b1959606a1b604082015260600190565b60208082526021908201527f427579206f72646572206e6f6e63652074696d657374616d7020746f6f206c6f6040820152607760f81b606082015260800190565b60208082526024908201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f72206164646040820152637265737360e01b606082015260800190565b60208082526014908201527321b0b63632b91036bab9ba1031329030b236b4b760611b604082015260600190565b60208082526027908201527f41737365742063616e6e6f742068617665206d6f7265207468616e20333220646040820152666563696d616c7360c81b606082015260800190565b60208082526034908201527f53656c6c206f72646572206d61726b65742073796d626f6c2061646472657373604082015273040e4cae6ded8eae8d2dedc40dad2e6dac2e8c6d60631b606082015260800190565b602080825260159082015274496e76616c696420746f6b656e206164647265737360581b604082015260600190565b60208082526030908201527f50726576696f757320696e76616c69646174696f6e206177616974696e67206360408201526f3430b4b710383937b830b3b0ba34b7b760811b606082015260800190565b60208082526028908201527f4e65742071756f746520706c757320666565206973206e6f7420657175616c20604082015267746f2067726f737360c01b606082015260800190565b6020808252601f908201527f53656c6c206f72646572206c696d697420707269636520657863656564656400604082015260600190565b602080825260149082015273125b9d985b1a59081d1bdad95b881cde5b589bdb60621b604082015260600190565b60208082526011908201527015d85b1b195d081b9bdd08195e1a5d1959607a1b604082015260600190565b60208082526016908201527f496e76616c69642077616c6c6574206164647265737300000000000000000000604082015260600190565b6020808252602b908201527f496d706c6965642071756f746520706970207175616e74697479206f7665726660408201526a1b1bdddcc81d5a5b9d0d8d60aa1b606082015260800190565b60208082526048908201527f546f6b656e20636f6e74726163742072657475726e6564207472616e7366657260408201527f207375636365737320776974686f75742065787065637465642062616c616e6360608201526765206368616e676560c01b608082015260a00190565b602080825260139082015272109390881cde5b589bdb081c995cd95c9d9959606a1b604082015260600190565b60208082526019908201527f4275792077616c6c657420657869742066696e616c697a656400000000000000604082015260600190565b60208082526028908201527f51756f7465207175616e74697479206d7573742062652067726561746572207460408201526768616e207a65726f60c01b606082015260800190565b6020808252600d908201526c15d85b1b195d08195e1a5d1959609a1b604082015260600190565b602080825260149082015273139bc818985b185b98d948199bdc88185cdcd95d60621b604082015260600190565b6020808252601e908201527f4d75737420626520646966666572656e742066726f6d2063757272656e740000604082015260600190565b60208082526027908201527f42617365207175616e74697479206d7573742062652067726561746572207468604082015266616e207a65726f60c81b606082015260800190565b6000602082528251151560208301526001600160a01b036020840151166040830152604083015160c06060840152615e4060e0840182614a21565b60ff60608601511660808501526080850151151560a08501526001600160401b0360a08601511660c0850152809250505092915050565b6001600160801b039390931683526001600160401b03919091166020830152604082015260600190565b90815260200190565b918252602082015260400190565b6001600160401b0391909116815260200190565b60006001600160401b03808816835260a06020840152615eef60a0840188614a21565b9581166040840152939093166060820152608001525092915050565b6040518181016001600160401b0381118282101715615f2957600080fd5b604052919050565b60005b83811015615f4c578181015183820152602001615f34565b8381111561091d5750506000910152565b6001600160a01b0381168114615f7257600080fd5b5056fe496e76616c69642077616c6c6574207369676e617475726520666f7220627579206f72646572496e76616c69642077616c6c6574207369676e617475726520666f722073656c6c206f72646572a264697066735822122047320e0b0b3f8ba0d6c8567653417f0196b8c76c378d33d1ec502161d92adbc664736f6c63430006080033

Deployed ByteCode Sourcemap

1220:38714:5:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12:1:-1;9;2:12;35000:281:5;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;35000:281:5;;;;;;;;:::i;:::-;;11683:85;;5:9:-1;2:2;;;27:1;24;17:12;2:2;11683:85:5;;;:::i;:::-;;;;;;;;;;;;;;;;37488:151;;5:9:-1;2:2;;;27:1;24;17:12;2:2;37488:151:5;;;:::i;8961:422::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;8961:422:5;;;;;;;;:::i;:::-;;;;;;;;9764:465;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;9764: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;13441:417::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;13441:417:5;;;;;;;;:::i;34396:248::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;34396:248:5;;;;;;;;:::i;21966:187::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;21966:187:5;;;:::i;23109:1171::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;23109:1171:5;;;;;;;;:::i;18016:2154::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;18016:2154:5;;;;;;;;:::i;733:222:8:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;733:222:8;;;;;;;;:::i;35547:209:5:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;35547:209:5;;;;;;;;:::i;36352:215::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;36352:215:5;;;;;;;;:::i;:::-;;;;;;;;8216:339;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;8216:339:5;;;;;;;;:::i;21030:778::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;21030:778:5;;;;;;;;:::i;12480:96::-;;;:::i;1097:74:8:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1097:74:8;;;:::i;12241:178:5:-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;12241:178:5;;;;;;;;:::i;:::-;;;;;;;;36849:428;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;36849:428:5;;;;;;;;:::i;16591:1168::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;16591:1168:5;;;;;;;;:::i;10597:238::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;10597:238:5;;;;;;;;:::i;12870:258::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;12870:258:5;;;;;;;;:::i;20476:292::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;20476:292:5;;;:::i;11201:370::-;;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;11201:370:5;;;;;;;;:::i;35000:281::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;;;;;;;;;35136:71:5::1;35176:12;35190:6;;35136:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;35136:14:5::1;::::0;:71;;-1:-1:-1;35198:8:5;;-1:-1:-1;;35136:71:5::1;:39;:71;:::i;:::-;35245:12;-1:-1:-1::0;;;;;35218:58:5::1;;35259:6;;35267:8;35218:58;;;;;;;;;;;;;;;;;35000:281:::0;;;;:::o;11683:85::-;11753:10;;-1:-1:-1;;;;;11753:10:5;11683:85;;:::o;37488:151::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;37564:17:5::1;::::0;37546:50:::1;::::0;::::1;::::0;::::1;::::0;-1:-1:-1;;;;;37564:17:5;;::::1;::::0;::::1;::::0;37546:50:::1;;;;;;;;;;37602:17;:32:::0;;-1:-1:-1;;;;;;37602:32:5::1;::::0;;37488:151::o;8961:422::-;9076:7;-1:-1:-1;;;;;9099:22:5;;9091:57;;;;-1:-1:-1;;;9091:57:5;;;;;;;;;9155:26;;:::i;:::-;9184:59;:14;9225:12;9184:59;:33;:59;:::i;:::-;-1:-1:-1;;;;;9309:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;9356:14;;;;9155:88;;-1:-1:-1;9262:116:5;;-1:-1:-1;;;;;9309:37:5;;;;9262;:116::i;:::-;9249:129;;;8961:422;;;;;:::o;9764:465::-;9885:7;-1:-1:-1;;;;;9908:22:5;;9900:57;;;;-1:-1:-1;;;9900:57:5;;;;;;;;;9964:26;;:::i;:::-;9993:90;10033:11;;9993:90;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;10052:25:5;;-1:-1:-1;10052:23:5;;-1:-1:-1;;10052:25:5:i;:::-;9993:14;;:90;;:32;:90;:::i;:::-;-1:-1:-1;;;;;10149:23:5;;;;;;;:15;:23;;;;;;;;10173:18;;;;10149:43;;;;;;;;;;10202:14;;;;9964:119;;-1:-1:-1;10102:122:5;;-1:-1:-1;;;;;10149:43:5;;;;10102:37;:122::i;:::-;10089:135;;;9764:465;;;;;;:::o;7507:455::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;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:8;7507:455:5::0;:::o;6843:249::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;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;13441:417::-;13557:19;13593:81;13635:11;;13593:81;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;13648:25:5;;-1:-1:-1;13648:23:5;;-1:-1:-1;;13648:25:5:i;13593:81::-;:103;;;;-1:-1:-1;;;;;;13716:37:5;;13708:74;;;;-1:-1:-1;;;13708:74:5;;;;;;;;;13789:64;13797:10;13817:12;13832:20;13789:7;:64::i;:::-;13441:417;;;;:::o;34396:248::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;34521:60:5::1;34550:12;34564:6;;34521:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;34521:14:5::1;::::0;:60;;-1:-1:-1;34572:8:5;;-1:-1:-1;;34521:60:5::1;:28;:60;:::i;:::-;34608:12;-1:-1:-1::0;;;;;34592:47:5::1;;34622:6;;34630:8;34592:47;;;;;;;;;21966:187:::0;22029:10;22016:24;;;;:12;:24;;;;;:31;;;22008:61;;;;-1:-1:-1;;;22008:61:5;;;;;;;;;22096:10;22083:24;;;;:12;:24;;;;;;22076:31;;-1:-1:-1;;22076:31:5;;;;;;;;22119:29;;;22083:24;22119:29;21966:187::o;23109:1171::-;37697:17;;-1:-1:-1;;;;;37697:17:5;37683:10;:31;37675:68;;;;-1:-1:-1;;;37675:68:5;;;;;;;;;23281:40:::1;23303:3;:17;;;23281:21;:40::i;:::-;23280:41;23265:97;;;;-1:-1:-1::0;;;23265:97:5::1;;;;;;;;;23384:41;23406:4;:18;;;23384:21;:41::i;:::-;23383:42;23368:99;;;;-1:-1:-1::0;;;23368:99:5::1;;;;;;;;;23509:4;:18;;;-1:-1:-1::0;;;;;23488:39:5::1;:3;:17;;;-1:-1:-1::0;;;;;23488:39:5::1;;;23473:94;;;;-1:-1:-1::0;;;23473:94:5::1;;;;;;;;;23574:35;23592:3;23597:4;23603:5;23574:17;:35::i;:::-;23615:37;23635:3;23640:4;23646:5;23615:19;:37::i;:::-;23658:30;23678:3;23683:4;23658:19;:30::i;:::-;23695:15;23712:16:::0;23732:65:::1;23763:3;23774:4;23786:5;23732:23;:65::i;:::-;23694:103;;;;23803:24;23821:5;23803:17;:24::i;:::-;23834:64;23862:3;23867:7;23876:4;23882:8;23892:5;23834:27;:64::i;:::-;23904:40;23927:3;23932:4;23938:5;23904:22;:40::i;:::-;24057:5;:22;;;23956:319;;;;;;;;;::::0;;;;::::1;::::0;;24028:21;;23956:319;;::::1;::::0;24028:21;23956:319:::1;;;;;;;;;;;23977:3;:17;;;24002:4;:18;;;24087:5;:21;;;24116:5;:22;;;24146:5;:29;;;24183:5;:30;;;24221:5;:17;;;24246:7;24261:8;23956:319;;;;;;;;;;;;;;;;;;;;;;;37749:1;;23109:1171:::0;;;:::o;18016:2154::-;37697:17;;-1:-1:-1;;;;;37697:17:5;37683:10;:31;37675:68;;;;-1:-1:-1;;;37675:68:5;;;;;;;;;18151:47:::1;18173:10;:24;;;18151:21;:47::i;:::-;18150:48;18142:74;;;;-1:-1:-1::0;;;18142:74:5::1;;;;;;;;;6116:8;-1:-1:-1::0;;;;;18237:109:5::1;:69;18255:10;:23;;;18280:10;:25;;;18237:17;:69::i;:::-;-1:-1:-1::0;;;;;18237:109:5::1;;;18222:164;;;;-1:-1:-1::0;;;18222:164:5::1;;;;;;;;;18392:22;18417:39;18445:10;18417:27;:39::i;:::-;18478:42;::::0;;;:26:::1;:42;::::0;;;;;18392:64;;-1:-1:-1;18478:42:5::1;;18477:43;18462:96;;;;-1:-1:-1::0;;;18462:96:5::1;;;;;;;;;18649:26;;:::i;:::-;18713:29;18678:25:::0;;:64:::1;::::0;::::1;;;;;;;:271;;18925:23;::::0;::::1;::::0;18891:58:::1;::::0;:14:::1;::::0;:58:::1;:33;:58;:::i;:::-;18678:271;;;18751:131;18793:10;:22;;;18825:49;18857:10;:16;;;18825:31;:49::i;18751:131::-;18649:300;;19004:29;19036:66;19073:10;:23;;;19036:10;:25;;;-1:-1:-1::0;;;;;19036:29:5::1;;;:66;;;;:::i;:::-;19004:98;;19108:36;19147:84;19192:22;19216:5;:14;;;19147:44;:84::i;:::-;19351:25;::::0;::::1;::::0;19287:31:::1;::::0;;::::1;::::0;-1:-1:-1;;;;;19271:48:5;;::::1;19237:31;19271:48:::0;;;:15:::1;:48;::::0;;;;;;19320:18;;::::1;::::0;19271:68;;::::1;::::0;;;;;;;;;;19108:123;;-1:-1:-1;19237:31:5;19271:106:::1;::::0;-1:-1:-1;;;;;19271:68:5;;::::1;::::0;:106:::1;:79;:106;:::i;:::-;19237:140;;19383:38;19424:86;19469:24;19495:5;:14;;;19424:44;:86::i;:::-;19533:24;::::0;;::::1;::::0;-1:-1:-1;;;;;19517:41:5;;::::1;;::::0;;;:15:::1;:41;::::0;;;;;;19559:25;;::::1;::::0;;19517:68;::::1;::::0;;;;;;;;:95;;-1:-1:-1;;19517:95:5::1;-1:-1:-1::0;;;;;19517:95:5;;::::1;::::0;;;::::1;::::0;;;19734:23:::1;::::0;::::1;::::0;19691:10:::1;::::0;;::::1;19675:27:::0;;;;;;;;19703:18;;19675:47;;::::1;::::0;;;;;;;;19383:127;;-1:-1:-1;19675:88:5::1;::::0;:47:::1;::::0;:51:::1;:88::i;:::-;19634:10;::::0;-1:-1:-1;;;;;19634:10:5;;::::1;19618:27;::::0;;;:15:::1;:27;::::0;;;;;;;19646:25;;::::1;::::0;;19618:54;::::1;::::0;;;;;;;;:145;;-1:-1:-1;;19618:145:5::1;-1:-1:-1::0;;;;;19618:145:5;;;::::1;::::0;;;::::1;::::0;;;19781:10:::1;::::0;19809:24;;::::1;::::0;19841:18;;19770:131;;-1:-1:-1;;;19770:131:5;;19781:10;::::1;::::0;19770:31:::1;::::0;:131:::1;::::0;19841:18;19867:28;;19770:131:::1;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24::::0;17:12:::1;2:2;19770: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;;;19908:42:5::1;::::0;;;:26:::1;:42;::::0;;;;;;;;:49;;-1:-1:-1;;19908:49:5::1;19953:4;19908:49;::::0;;20018:18;::::1;::::0;19986:24;;::::1;::::0;20044:12;;::::1;::::0;20064:25:::1;::::0;::::1;::::0;19969:196;;-1:-1:-1;;;;;19969:196:5;;::::1;::::0;-1:-1:-1;19969:196:5;;;::::1;::::0;::::1;::::0;::::1;::::0;20044:12;;20064:25;20097:24;;20129:30;;19969:196:::1;;;;;;;;;;37749:1;;;;;;18016:2154:::0;:::o;733:222:8:-;254:10;-1:-1:-1;;;;;268:6:8;254:20;;246:53;;;;-1:-1:-1;;;246:53:8;;;;;;;;;-1:-1:-1;;;;;802:24:8;::::1;794:59;;;;-1:-1:-1::0;;;794:59:8::1;;;;;;;;;879:6;::::0;-1:-1:-1;;;;;867:18:8;;::::1;879:6:::0;::::1;867:18;;859:67;;;;-1:-1:-1::0;;;859:67:8::1;;;;;;;;;933:6;:17:::0;;-1:-1:-1;;;;;;933:17:8::1;-1:-1:-1::0;;;;;933:17:8;;;::::1;::::0;;;::::1;::::0;;733:222::o;35547:209:5:-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;35651:51:5::1;35681:12;35695:6;;35651:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16:::0;::::1;74:27:::0;;;;-1:-1;35651:14:5::1;::::0;:51;;-1:-1:-1;;35651:51:5::1;:29;:51;::::0;-1:-1:-1;35651:51:5:i:1;:::-;35730:12;-1:-1:-1::0;;;;;35713:38:5::1;;35744:6;;35713:38;;;;;;;;;;;;;;;;35547:209:::0;;;:::o;36352:215::-;36465:20;;:::i;:::-;36502:60;36535:11;;36502:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;36502:14:5;;:60;-1:-1:-1;36548:13:5;;-1:-1:-1;;36502:60:5;:32;:60;:::i;:::-;36495:67;36352:215;-1:-1:-1;;;;36352:215:5:o;8216:339::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;-1:-1:-1;;;;;8293:28:5;::::1;8285:63;;;;-1:-1:-1::0;;;8285:63:5::1;;;;;;;;;8378:10;::::0;-1:-1:-1;;;;;8362:26:5;;::::1;8378:10:::0;::::1;8362:26;;8354:69;;;;-1:-1:-1::0;;;8354:69:5::1;;;;;;;;;8453:10;::::0;;-1:-1:-1;;;;;8469:25:5;;::::1;-1:-1:-1::0;;;;;;8469:25:5;::::1;;::::0;;;8506:44:::1;::::0;8453:10;::::1;::::0;8506:44:::1;::::0;::::1;::::0;8453:10;;8482:12;;8506:44:::1;;21030:778:::0;21097:33;21119:10;21097:21;:33::i;:::-;21089:71;;;;-1:-1:-1;;;21089:71:5;;;;;;;;;21167:26;;:::i;:::-;21196:59;:14;21237:12;21196:59;:33;:59;:::i;:::-;21300:10;21261:20;21284:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;21284:41:5;;;;;;;;;;21427:14;;;;21167:88;;-1:-1:-1;;;;;;21284:41:5;;21361:86;;21284:41;;21361:37;:86::i;:::-;21331:116;;21484:1;21462:19;:23;21454:56;;;;-1:-1:-1;;;21454:56:5;;;;;;;;;21532:10;21560:1;21516:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;21516:41:5;;;;;;;;;;;:45;;-1:-1:-1;;21516:45:5;;;21578:10;;21567:102;;-1:-1:-1;;;21567:102:5;;21578:10;;;21567:31;;:102;;21532:10;21544:12;;21644:19;;21567:102;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;21567:102:5;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;21567:102:5;;;;21726:12;-1:-1:-1;;;;;21681:122:5;21708:10;-1:-1:-1;;;;;21681:122:5;;21746:5;:12;;;21766:13;21787:1;21796;21681:122;;;;;;;;;;;;;;;;;;21030:778;;;;:::o;12480:96::-;12527:44;12535:10;12555:3;12561:9;12527:7;:44::i;:::-;12480:96::o;1097:74:8:-;254:10;-1:-1:-1;;;;;268:6:8;254:20;;246:53;;;;-1:-1:-1;;;246:53:8;;;;;;;;;1162:3:::1;1145:21:::0;;-1:-1:-1;;;;;;1145:21:8::1;::::0;;1097:74::o;12241:178:5:-;12343:6;12366:48;;;:37;:48;;;;;;-1:-1:-1;;;;;12366:48:5;12241:178;;;;:::o;36849:428::-;361:6:8;;-1:-1:-1;;;;;361:6:8;347:10;:20;339:53;;;;-1:-1:-1;;;339:53:8;;;;;;;;;-1:-1:-1;;;;;36934:35:5;::::1;36926:70;;;;-1:-1:-1::0;;;36926:70:5::1;;;;;;;;;37040:17;::::0;-1:-1:-1;;;;;37017:40:5;;::::1;37040:17:::0;::::1;37017:40;;37002:101;;;;-1:-1:-1::0;;;37002:101:5::1;;;;;;;;;37139:17;::::0;;-1:-1:-1;;;;;37162:39:5;;::::1;-1:-1:-1::0;;;;;;37162:39:5;::::1;;::::0;;;37213:59:::1;::::0;37139:17;::::1;::::0;37213:59:::1;::::0;::::1;::::0;37139:17;;37182:19;;37213:59:::1;;16591:1168:::0;16651:20;16674:38;16706:5;16674:31;:38::i;:::-;16651:61;;16897:22;:20;:22::i;:::-;-1:-1:-1;;;;;16881:38:5;:13;-1:-1:-1;;;;;16881:38:5;;16866:102;;;;-1:-1:-1;;;16866:102:5;;;;;;;;;16999:10;16979:31;;;;:19;:31;;;;;:38;;;16975:353;;;17064:10;17044:31;;;;:19;:31;;;;;:45;-1:-1:-1;;;;;17044:61:5;;;:45;;;;;:61;17027:133;;;;-1:-1:-1;;;17027:133:5;;;;;;;;;17205:10;17185:31;;;;:19;:31;;;;;:52;;;17241:12;-1:-1:-1;17185:68:5;17168:153;;;;-1:-1:-1;;;17168:153:5;;;;;;;;;17488:23;;17551:84;;;;;;;;17576:4;17551:84;;;-1:-1:-1;;;;;17551:84:5;;;;;;;;;;17473:12;:38;;;17551:84;;;;;;17537:10;-1:-1:-1;17517:31:5;;;:19;:31;;;;;;;:118;;;;;;-1:-1:-1;;17517:118:5;;;;;;;-1:-1:-1;;17517:118:5;;;;;;;;;;;;;;;;;;;;;;;;;17647:107;;17473:38;;17537:10;17647:107;;;;17694:5;;17551:84;;17473:38;;17647:107;;10597:238;10706:6;-1:-1:-1;;;;;10730:22:5;;10722:57;;;;-1:-1:-1;;;10722:57:5;;;;;;;;;-1:-1:-1;;;;;;10793:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;;;-1:-1:-1;;;;;10793:37:5;;10597:238::o;12870:258::-;-1:-1:-1;;;;;12987:37:5;;12979:74;;;;-1:-1:-1;;;12979:74:5;;;;;;;;;13059:64;13067:10;13087:12;13102:20;13059:7;:64::i;:::-;12870:258;;:::o;20476:292::-;20535:10;20522:24;;;;:12;:24;;;;;:31;;;20521:32;20513:66;;;;-1:-1:-1;;;20513:66:5;;;;;;;;;20613:74;;;;;;;;20631:4;20613:74;;;20658:23;;;20643:12;:38;;;20613:74;;;;;;;20599:10;-1:-1:-1;20586:24:5;;;:12;:24;;;;;;;:101;;;;-1:-1:-1;;20586:101:5;;;;;;;;;;;;;;;;;20739:23;20699:64;;20599:10;;20699:64;;;;20724:38;;;20699:64;;;;;;;;;;20476:292::o;11201:370::-;11316:6;-1:-1:-1;;;;;11338:22:5;;11330:57;;;;-1:-1:-1;;;11330:57:5;;;;;;;;;11394:20;11417:79;11457:11;;11417:79;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;11470:25:5;;-1:-1:-1;11470:23:5;;-1:-1:-1;;11470:25:5:i;11417:79::-;:99;;;;;-1:-1:-1;;;;;11529:23:5;;;;;;;:15;:23;;;;;;:37;;;;;;;;;;;-1:-1:-1;;;;;11529:37:5;;-1:-1:-1;;11201: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;2943:405::-;3050:20;;:::i;:::-;-1:-1:-1;;;;;3084:28:1;;3080:69;;3129:13;:11;:13::i;:::-;3122:20;;;;3080:69;3155:26;;:::i;:::-;-1:-1:-1;;;;;3184:34:1;;;:20;:34;;;;;;;;;;;;3155:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3155:63:1;;;;;;;;;;;;;;;;;;;;;;;;;3184:34;;3155:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;3155:63:1;;;-1:-1:-1;;3155:63:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3155:63:1;;;;;;3239:12;;;;-1:-1:-1;3239:33:1;;;;;3255:5;:17;;;3239:33;3224:100;;;;-1:-1:-1;;;3224: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;38341:156:5:-;38460:15;38435:4;38453:39;38341:156;:::o;3712:691:1:-;3844:20;;:::i;:::-;3876:28;;;;;;;;;;;;;;-1:-1:-1;;;3876:28:1;;;3897:6;3876:13;:28::i;:::-;3872:69;;;3921:13;:11;:13::i;:::-;3914:20;;;;3872:69;3947:26;;:::i;:::-;4020:1;3983:4;:19;;4003:6;3983:27;;;;;;;;;;;;;;;;;;;;;:34;:38;3979:296;;;4036:7;4031:238;4053:4;:19;;4073:6;4053:27;;;;;;;;;;;;;;;;;;;;;:34;4049:38;;;;4031:238;;;4176:13;-1:-1:-1;;;;;4119:70:1;:4;:19;;4139:6;4119:27;;;;;;;;;;;;;;;;;;;;4147:1;4119:30;;;;;;;;;;;;;;;;;;;;;;;:53;;;;;;-1:-1:-1;;;;;4119:53:1;:70;4104:157;;4220:4;:19;;4240:6;4220:27;;;;;;;;;;;;;;;;;;;;4248:1;4220:30;;;;;;;;;;;;;;;;;;;4212:38;;;;;;;;4220:30;;;;;;;;4212:38;;;;;;;;;;;;;;-1:-1:-1;;;;;4212:38:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4212:38:1;;;;;;;;;;;;;;;;;;;;;;;;;4220:30;;4212:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;4212:38:1;;;-1:-1:-1;;4212:38:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4212:38:1;;;;;;;-1:-1:-1;4104:157:1;4089:3;;4031:238;;;;3979:296;4295:12;;:33;;;;;4311:5;:17;;;4295:33;4280:99;;;;-1:-1:-1;;;4280:99:1;;;;;;;;718:413:0;1078:20;1116:8;;;718:413::o;13862:2369:5:-;-1:-1:-1;;;;;14216:20:5;;;;;;:12;:20;;;;;:27;;;14215:28;14207:54;;;;-1:-1:-1;;;14207:54:5;;;;;;;;;14268:26;;:::i;:::-;14297:59;:14;14338:12;14297:59;:33;:59;:::i;:::-;14268:88;;14362:21;14386:93;14431:20;14459:5;:14;;;14386:37;:93::i;:::-;14362:117;;14510:1;14493:14;-1:-1:-1;;;;;14493:18:5;;14485:50;;;;-1:-1:-1;;;14485:50:5;;;;;;;;;14902:49;14954:76;14999:14;15015:5;:14;;;14954:44;:76::i;:::-;14902:128;-1:-1:-1;;;;;;15278:28:5;;;15274:181;;15316:132;15353:6;15376:12;15399:41;15316:27;:132::i;:::-;15537:10;;15504:118;;-1:-1:-1;;;;;15537:10:5;15555:12;15575:41;15504:25;:118::i;:::-;-1:-1:-1;;;;;15663:23:5;;;15629:31;15663:23;;;:15;:23;;;;;;;;:37;;;;;;;;;;;;:69;;-1:-1:-1;;;;;15663:37:5;15712:14;15663:69;:41;:69;:::i;:::-;15629:103;;15738:38;15779:86;15824:24;15850:5;:14;;;15779:44;:86::i;:::-;-1:-1:-1;;;;;15927:23:5;;;;;;;:15;:23;;;;;;;;:37;;;;;;;;;;;:64;;-1:-1:-1;;15927:64:5;-1:-1:-1;;;;;15927:64:5;;;;;;;;;;15997:13;:15;;-1:-1:-1;;;;15997:15:5;;-1:-1:-1;;;15997:15:5;;;;;;-1:-1:-1;15997:15:5;;;;;;;;;;;16096:12;;;;16024:202;;15738:127;;-1:-1:-1;16024:202:5;;;;;;;;;;;;16076:12;-1:-1:-1;;;;;16024:202:5;16062:6;-1:-1:-1;;;;;16024:202:5;;16041:13;;;;;;;;;-1:-1:-1;;;;;16041:13:5;16116:5;:12;;;16136:14;16158:24;16190:30;16024:202;;;;;;;;;;;;;;;;;;;13862: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;22157:202:5:-;-1:-1:-1;;;;;22265:20:5;;22227:4;22265:20;;;:12;:20;;;;;22298:11;;;;:56;;;;-1:-1:-1;22313:25:5;;;22342:12;-1:-1:-1;22313:41:5;;22291:63;-1:-1:-1;;22157:202:5:o;27973:1929::-;28157:5;:23;;;-1:-1:-1;;;;;28131:49:5;:5;:22;;;-1:-1:-1;;;;;28131:49:5;;;28116:119;;;;-1:-1:-1;;;28116:119:5;;;;;;;;;28271:33;;:::i;:::-;28307:117;28347:5;:21;;;28376:42;28408:3;:9;;;28376:31;:42::i;28307:117::-;28271:153;;28430:34;;:::i;:::-;28467:118;28507:5;:22;;;28537:42;28569:3;:9;;;28537:31;:42::i;28467:118::-;28430:155;;28635:5;:22;;;-1:-1:-1;;;;;28606:51:5;:12;:25;;;-1:-1:-1;;;;;28606:51:5;;:116;;;;;28699:5;:23;;;-1:-1:-1;;;;;28669:53:5;:13;:26;;;-1:-1:-1;;;;;28669:53:5;;28606:116;28591:198;;;;-1:-1:-1;;;28591:198:5;;;;;;;;;28826:34;;:::i;:::-;28863:118;28903:5;:21;;;28932:43;28964:4;:10;;;28932:31;:43::i;28863:118::-;28826:155;;28987:35;;:::i;:::-;29025:119;29065:5;:22;;;29095:43;29127:4;:10;;;29095:31;:43::i;29025:119::-;28987:157;;29195:5;:22;;;-1:-1:-1;;;;;29165:52:5;:13;:26;;;-1:-1:-1;;;;;29165:52:5;;:118;;;;;29260:5;:23;;;-1:-1:-1;;;;;29229:54:5;:14;:27;;;-1:-1:-1;;;;;29229:54:5;;29165:118;29150:201;;;;-1:-1:-1;;;29150:201:5;;;;;;;;;29431:5;:22;;;-1:-1:-1;;;;;29401:52:5;:5;:26;;;-1:-1:-1;;;;;29401:52:5;;:117;;;;29495:5;:23;;;-1:-1:-1;;;;;29465:53:5;:5;:26;;;-1:-1:-1;;;;;29465:53:5;;29401:117;29386:184;;;;-1:-1:-1;;;29386:184:5;;;;;;;;;29621:5;:22;;;-1:-1:-1;;;;;29591:52:5;:5;:26;;;-1:-1:-1;;;;;29591:52:5;;:117;;;;29685:5;:23;;;-1:-1:-1;;;;;29655:53:5;:5;:26;;;-1:-1:-1;;;;;29655:53:5;;29591:117;29576:184;;;;-1:-1:-1;;;29576:184:5;;;;;;;;;29811:5;:26;;;-1:-1:-1;;;;;29781:56:5;:5;:26;;;-1:-1:-1;;;;;29781:56:5;;;29766:131;;;;-1:-1:-1;;;29766:131:5;;;;;;;;;27973:1929;;;;;;;:::o;29906:909::-;30098:1;30066:5;:29;;;-1:-1:-1;;;;;30066:33:5;;30051:103;;;;-1:-1:-1;;;30051:103:5;;;;;;;;;30208:1;30175:5;:30;;;-1:-1:-1;;;;;30175:34:5;;30160:105;;;;-1:-1:-1;;;30160:105:5;;;;;;;;;30276:31;30293:3;:13;;;30276:16;:31::i;:::-;30272:265;;;30450:5;:30;;;-1:-1:-1;;;;;30334:146:5;:112;30375:5;:29;;;30416:3;:20;;;30334:29;:112::i;:::-;-1:-1:-1;;;;;30334:146:5;;;30317:213;;;;-1:-1:-1;;;30317:213:5;;;;;;;;;30547:32;30564:4;:14;;;30547:16;:32::i;:::-;30543:268;;;30723:5;:30;;;-1:-1:-1;;;;;30606:147:5;:113;30647:5;:29;;;30688:4;:21;;;30606:29;:113::i;:::-;-1:-1:-1;;;;;30606:147:5;;;30589:215;;;;-1:-1:-1;;;30589:215:5;;;;;;;;;29906:909;;;:::o;33128:453::-;33309:46;33337:3;:17;;;33309:27;:46::i;:::-;-1:-1:-1;;;;;33256:99:5;:42;33288:3;:9;;;33256:31;:42::i;:::-;-1:-1:-1;;;;;33256:99:5;;33241:163;;;;-1:-1:-1;;;33241:163:5;;;;;;;;;33479:47;33507:4;:18;;;33479:27;:47::i;:::-;-1:-1:-1;;;;;33425:101:5;:43;33457:4;:10;;;33425:31;:43::i;:::-;-1:-1:-1;;;;;33425:101:5;;33410:166;;;;-1:-1:-1;;;33410:166:5;;;;;;;;32196:346;32348:7;32357;32372:20;32395:34;32418:3;32423:5;32395:22;:34::i;:::-;32372:57;;32435:21;32459:35;32482:4;32488:5;32459:22;:35::i;:::-;32509:12;;;;-1:-1:-1;32196:346:5;;-1:-1:-1;;;;;32196:346:5:o;30819:1373::-;30893:31;30963:5;:22;;;-1:-1:-1;;;;;30927:58:5;:5;:26;;;-1:-1:-1;;;;;30927:58:5;;:135;;31032:5;:30;;;30927:135;;;30994:5;:29;;;30927:135;30893:169;;6049:8;-1:-1:-1;;;;;31083:124:5;:97;31110:5;:28;;;31148:24;31083:17;:97::i;:::-;-1:-1:-1;;;;;31083:124:5;;;31068:174;;;;-1:-1:-1;;;31068:174:5;;;;;;;;;31249:31;31319:5;:22;;;-1:-1:-1;;;;;31283:58:5;:5;:26;;;-1:-1:-1;;;;;31283:58:5;;:135;;31388:5;:30;;;31283:135;;;31350:5;:29;;;31283:135;31249:169;;6049:8;-1:-1:-1;;;;;31439:124:5;:97;31466:5;:28;;;31504:24;31439:17;:97::i;:::-;-1:-1:-1;;;;;31439:124:5;;;31424:174;;;;-1:-1:-1;;;31424:174:5;;;;;;;;;31807:5;:29;;;-1:-1:-1;;;;;31620:216:5;:183;31691:5;:22;;;-1:-1:-1;;;;;31661:52:5;:5;:26;;;-1:-1:-1;;;;;31661:52:5;;:134;;31767:5;:28;;;31661:134;;;31726:5;:28;;;31661:134;31620:27;;;;-1:-1:-1;;;;;31620:31:5;;:183;:31;:183;:::i;:::-;-1:-1:-1;;;;;31620:216:5;;31605:286;;;;-1:-1:-1;;;31605:286:5;;;;;;;;;32101:5;:30;;;-1:-1:-1;;;;;31912:219:5;:185;31984:5;:23;;;-1:-1:-1;;;;;31954:53:5;:5;:26;;;-1:-1:-1;;;;;31954:53:5;;:135;;32061:5;:28;;;31954:135;;;32020:5;:28;;;31954:135;31912:28;;;;-1:-1:-1;;;;;31912:32:5;;:185;:32;:185;:::i;:::-;-1:-1:-1;;;;;31912:219:5;;31897:290;;;;-1:-1:-1;;;31897:290:5;;;;;;;;25846:336;26057:56;26083:8;26093:12;26107:5;26057:25;:56::i;:::-;26119:58;26145:9;26156:13;26171:5;26119:25;:58::i;:::-;25846:336;;;;;:::o;24396:1446::-;24732:29;;;;24670:18;;;;;-1:-1:-1;;;;;24654:35:5;;;;;;;:15;:35;;;;;;;24690:29;;;;24654:66;;;;;;;;;;;:108;;-1:-1:-1;;;;;24654:66:5;;;;:108;:77;:108;:::i;:::-;24601:18;;;;;-1:-1:-1;;;;;24585:35:5;;;;;;;:15;:35;;;;;;;24621:29;;;;;24585:66;;;;;;;;;;:177;;-1:-1:-1;;24585:177:5;-1:-1:-1;;;;;24585:177:5;;;;;;24957:27;;;;24896:17;;;;24880:34;;;;;;;;;;24915:29;;24880:65;;;;;;;;;;;;;;:105;;:65;;:76;:105::i;:::-;24828:17;;;;;;-1:-1:-1;;;;;24812:34:5;;;;;;;:15;:34;;;;;;;24847:29;;;;24812:65;;;;;;;;;:173;;-1:-1:-1;;24812:173:5;-1:-1:-1;;;;;24812:173:5;;;;;;25185:30;;;;25123:17;;25107:34;;;;;;;;;;25142:30;;;;25107:66;;;;;;;;;;;;;;:109;;:66;;:77;:109::i;:::-;25054:17;;;;;-1:-1:-1;;;;;25038:34:5;;;;;;;:15;:34;;;;;;;25073:30;;;;;25038:66;;;;;;;;;;:178;;-1:-1:-1;;25038:178:5;-1:-1:-1;;;;;25038:178:5;;;;;;25417:28;;;;25354:18;;;;25338:35;;;;;;;;;;25374:30;;25338:67;;;;;;;;;;;;;;:108;;:67;;:78;:108::i;:::-;25284:18;;;;;-1:-1:-1;;;;;25268:35:5;;;;;;;:15;:35;;;;;;;25304:30;;;;25268:67;;;;;;;;;:178;;-1:-1:-1;;25268:178:5;-1:-1:-1;;;;;25268:178:5;;;;;;25634:28;;;;25576:10;;;;25560:27;;;;;;;;25268:178;25588:33;;;25560:62;;;;;;;;;;;;;;:103;;:62;;:73;:103::i;:::-;25511:10;;;-1:-1:-1;;;;;25511:10:5;;;25495:27;;;;:15;:27;;;;;;;;25511:10;25523:33;;;25495:62;;;;;;;;;:168;;-1:-1:-1;;25495:168:5;-1:-1:-1;;;;;25495:168:5;;;;;;25808:28;;;;25750:10;;;;25734:27;;;;;;;;25762:33;;;;25734:62;;;;;;;;;;;:103;;:62;;:73;:103::i;:::-;25685:10;;-1:-1:-1;;;;;25685:10:5;;;25669:27;;;;:15;:27;;;;;;;;25697:33;;;;;;25669:62;;;;;;;;;;;;:168;;-1:-1:-1;;25669:168:5;-1:-1:-1;;;;;25669:168:5;;;;;;;;;-1:-1:-1;;24396:1446:5:o;38501:244::-;38588:6;38632:9;38702:38;38734:5;38702:27;-1:-1:-1;;;;;38702:7:5;;38632:9;38702:27;:7;:27;:::i;:::-;-1:-1:-1;;;;;38702:31:5;;:38;:31;:38;:::i;33585:421::-;33694:7;33711:22;33736:46;33771:10;33736:34;:46::i;:::-;33711:71;;33804:129;33841:14;33865:10;:26;;;33901:10;:24;;;33804:27;:129::i;:::-;33789:184;;;;-1:-1:-1;;;33789:184:5;;;;;;;;388:901:12;473:23;596:34;590:2;582:10;;;581:49;655:1;644:12;;636:40;;;;-1:-1:-1;;;636:40:12;;;;;;;;;794:34;788:2;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:12;;;-1:-1:-1;;;;;1185:41:12;;;:69;;;;:::i;:::-;1166:88;388:901;-1:-1:-1;;;;;;;388:901:12:o;1261:125:10:-;1317:6;1338:43;1342:1;1345;1338:43;;;;;;;;;;;;;;;;;:3;:43::i;859:158::-;915:6;940:5;;;-1:-1:-1;;;;;959:6:10;;;;;;;;951:46;;;;-1:-1:-1;;;951:46:10;;;;;;;;2206:597: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:61;;;;-1:-1:-1;;;2509:61:1;;;;;;;;;2720:15;2672:4;2720:31;;;-1:-1:-1;;;;;2682:70:1;:28;;;:70;2759:27;;:19;;;;:27;;2779:6;;2759:27;;;;;;;;;;;;;;;;;;;;27:10:-1;;39:1;23:18;;;45:23;;-1:-1;2759:39:1;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2759:39:1;;;-1:-1:-1;;;;;;2759:39:1;;;-1:-1:-1;;2759:39:1;;;;;;;;;;;;;;;;;;;;;;;2792:5;;2759:39;;;;;;;;:::i;39669:263:5:-;39876:15;39762:12;39869:41;39850:4;39868:59;39669:263;:::o;4741:181:1:-;4832:4;4914:1;4897:19;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;4897:19:1;;;4887:30;;;;;;4880:1;4863:19;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;4863:19:1;;;4853:30;;;;;;:64;4846:71;;4741:181;;;;:::o;4509:138::-;4554:20;;:::i;:::-;-1:-1:-1;4589:53:1;;;;;;;;4603:4;4589:53;;;-1:-1:-1;4589:53:1;;;;;;;;;;;;;;;;;-1:-1:-1;;;4589:53:1;;;;;;;;;;;;;;4630:2;4589:53;;;;;;;;;;;;;;;4509:138;:::o;2180:459:9:-;2238:7;2479:6;2475:45;;-1:-1:-1;2508:1:9;2501:8;;2475:45;2542:5;;;2546:1;2542;:5;:1;2565:5;;;;;:10;2557:56;;;;-1:-1:-1;;;2557:56:9;;;;;;;;3101:130;3159:7;3185:39;3189:1;3192;3185:39;;;;;;;;;;;;;;;;;: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;;;;;;;;37774:308:5;37861:4;37901:21;37888:9;:34;;;;;;;;;:83;;;-1:-1:-1;37945:26:5;37932:9;:39;;;;;;;;;37888:83;:135;;;-1:-1:-1;37994:29:5;37981:9;:42;;;;;;;;;37888:135;:189;;;-1:-1:-1;38046:31:5;38033:44;:9;:44;;;;;;;;;37875:202;37774:308;-1:-1:-1;;37774:308:5:o;38749:573::-;38874:6;39014:5;38874:6;39063:92;39014:5;39063:65;-1:-1:-1;;;;;39063:27:5;;;;39102:25;;39063:65;:38;:65;:::i;:::-;:76;:92;:76;:92;:::i;:::-;39026:129;;-1:-1:-1;;;39176:26:5;:34;39161:108;;;;-1:-1:-1;;;39161:108:5;;;;;;;;39326:339;-1:-1:-1;;;;;39447:34:5;;39420:6;39447:34;;;:19;:34;;;;;:41;;;:122;;;;-1:-1:-1;;;;;;39498:34:5;;;;;;:19;:34;;;;;:55;;;39557:12;-1:-1:-1;39498:71:5;39447:122;39436:210;;;-1:-1:-1;;;;;;39591:34:5;;;;;;:19;:34;;;;;:48;;;;-1:-1:-1;;;;;39591:48:5;39584:55;;39436:210;-1:-1:-1;39659:1:5;39326:339;;;:::o;32546:578::-;32668:7;32683:17;32703:107;32740:5;32753;:21;;;32782:5;:22;;;32703:29;:107::i;:::-;32683:127;;32832:114;32869:9;32888:5;:21;;;32919:5;:19;;;32832:27;:114::i;:::-;32968:19;32954:5;:10;;;:33;;;;;;;;;:136;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;32817:279;;;;;-1:-1:-1;;;32817:279:5;;;;;;;;;;-1:-1:-1;33110:9:5;32546:578;-1:-1:-1;;;32546:578:5:o;26277:1671::-;26426:32;;;;:21;:32;;;;;;;;26425:33;26417:65;;;;-1:-1:-1;;;26417:65:5;;;;;;;;;26583:30;26842:5;:23;;;26838:518;;;26892:34;26910:5;:15;;;26892:17;:34::i;:::-;26875:120;;;;-1:-1:-1;;;26875:120:5;;;;;;;;;27073:48;;;;:37;:48;;;;;;27029:30;;;;:100;;-1:-1:-1;;;;;27029:34:5;;;;27073:48;27029:100;:34;:100;:::i;:::-;27003:126;;26838:518;;;27293:48;;;;:37;:48;;;;;;27250:29;;;;:99;;-1:-1:-1;;;;;27250:33:5;;;;27293:48;27250:99;:33;:99;:::i;:::-;27224:125;;26838:518;27404:5;:20;;;-1:-1:-1;;;;;27377:47:5;:23;-1:-1:-1;;;;;27377:47:5;;;27362:94;;;;-1:-1:-1;;;27362:94:5;;;;;;;;;27493:5;:20;;;-1:-1:-1;;;;;27467:46:5;:23;-1:-1:-1;;;;;27467:46:5;;27463:481;;;27597:48;;;;:37;:48;;;;;:74;;-1:-1:-1;;27597:74:5;-1:-1:-1;;;;;27597:74:5;;;;;27463:481;;;-1:-1:-1;;27842:48:5;;;;:37;:48;;;;;;;;27835:55;;-1:-1:-1;;27835:55:5;;;27898:21;:32;;;;;:39;;-1:-1:-1;;27898:39:5;-1:-1:-1;27898:39:5;;;-1:-1:-1;26277:1671:5:o;2091:415:10:-;2147:6;-1:-1:-1;;;;;2371:6:10;;2367:35;;-1:-1:-1;2394:1:10;2387:8;;2367:35;2419:5;;;-1:-1:-1;;;;;2438:10:10;;;;:5;;;;;;;;;;;;-1:-1:-1;;;;;2438:10:10;;2430:56;;;;-1:-1:-1;;;2430:56:10;;;;;;;;2937:121;2993:6;3014:39;3018:1;3021;3014:39;;;;;;;;;;;;;;;;;:3;:39::i;1854:628:11:-;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:11;;;2197:169;;;2287:10;:22;;;2270:40;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;2270:40:11;;;2197:169;2378:39;2391:10;:25;;;-1:-1:-1;;;;;2378:39:11;:12;:39::i;:::-;2429:10;:30;;;2009:460;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;2009:460:11;;;1990:487;;;;;;1977:500;;1854:628;;;:::o;330:219::-;449:4;538:6;-1:-1:-1;;;;;474:70:11;:60;488:34;517:4;488:28;:34::i;:::-;524:9;474:13;:60::i;:::-;-1:-1:-1;;;;;474:70:11;;;330:219;-1:-1:-1;;;;330:219:11:o;1686:185:10:-;1786:6;1813:1;-1:-1:-1;;;;;1808:6:10;:1;-1:-1:-1;;;;;1808:6:10;;;1816:12;1800:29;;;;;-1:-1:-1;;;1800:29:10;;;;;;;;;;-1:-1:-1;;;1846:5:10;;;1686:185::o;3713:272:9:-;3799:7;3833:12;3826:5;3818:28;;;;-1:-1:-1;;;3818:28:9;;;;;;;;;;;3856:9;3872:1;3868;:5;;;;;;;3713:272;-1:-1:-1;;;;;3713:272:9:o;1321:134::-;1379:7;1405:43;1409:1;1412;1405:43;;;;;;;;;;;;;;;;;:3;:43::i;553:1297:11:-;701:7;731:5;:26;;;:31;;761:1;731:31;716:94;;;;-1:-1:-1;;;716:94:11;;;;;;;;;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:11;:12;:34::i;:::-;978:402;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;978:402:11;;;1422:5;:23;;;1484:1;1459:5;:22;;;-1:-1:-1;;;;;1459:26:11;;:98;;;;;;;;;;;;;;;;;1502:36;1515:5;:22;;;-1:-1:-1;;;;;1502:36:11;:12;:36::i;:::-;1595:1;1571:5;:21;;;-1:-1:-1;;;;;1571:25:11;;:96;;;;;;;;;;;;;;;;;1613:35;1626:5;:21;;;-1:-1:-1;;;;;1613:35:11;: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:11;;;;950:887;;;49:4:-1;950:887:11;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;950:887:11;;;829:1016;;;;;;816:1029;;553:1297;;;;;:::o;38086:251:5:-;38174:4;;38201:9;:35;;;;;;;;;:82;;;-1:-1:-1;38259:24:5;38246:9;:37;;;;;;;;;38201:82;:131;;;-1:-1:-1;38306:26:5;38293:39;;3545:328:10;3645:6;3736:12;-1:-1:-1;;;;;3729:5:10;;3721:28;;;;-1:-1:-1;;;3721:28:10;;;;;;;;;;;3755:8;3770:1;-1:-1:-1;;;;;3766:5:10;:1;-1:-1:-1;;;;;3766:5:10;;;;;3829:776:11;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:11;4162:92;4259:8;;4299:20;4259:8;-1:-1:-1;;;;;4322:17:11;;2:2:-1;;;;27:1;24;17:12;2:2;4322:17:11;;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;108:14;4322:17:11;87:42:-1;143:17;;-1:-1;4322:17:11;-1:-1:-1;4299:40:11;-1:-1:-1;4362:6:11;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:11;;;;;;;;;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:11;;;;;;;;-1:-1:-1;4556:2:11;4548:10;;;;4390:177;-1:-1:-1;;4377:3:11;4345:228;;;-1:-1:-1;4592:7:11;3829:776;-1:-1:-1;;;;3829:776:11: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;2659:66;2646:79;;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:9:-;1832:7;1867:12;1859:6;;;;1851:29;;;;-1:-1:-1;;;1851:29:9;;;;;;;;;2832:805:11;3048:10;;;;;;;;;;;;;-1:-1:-1;;;3048:10:11;;;;3212:23;;3187:22;;2944:13;;3004:10;;3048;;3102:11;;2944:13;;3187:48;;:69;-1:-1:-1;;;;;3167:97:11;;2:2:-1;;;;27:1;24;17:12;2:2;3167:97:11;;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;108:14;3167:97:11;87:42:-1;143:17;;-1:-1;3167:97:11;-1:-1:-1;3121:149:11;-1:-1:-1;3277:9:11;;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:11;;;;;;;;-1:-1:-1;3348:3:11;;;;;3308:103;;;3468:11;3480:1;3468:14;;;;;;;;;;;;;;;;3443:17;3461:3;;;;;;3443:22;;;;;;;;;;;:39;-1:-1:-1;;;;;3443:39:11;;;;;;;;;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:11;;;;;;;;-1:-1:-1;3530:3:11;;;;;3489:105;;;-1:-1:-1;3614:17:11;;2832:805;-1:-1:-1;;;;;;;2832:805:11:o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;5:130;72:20;;97:33;72:20;97:33;;295:124;359:20;;92799:13;;92792:21;96315:32;;96305:2;;96361:1;;96351: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;;;;;91109:6;91106:30;91103:2;;;-1:-1;;91139:12;91103:2;742:64;91212:9;91193:17;;-1:-1;;91189:33;91280:4;91270:15;742:64;;;733:73;;826:6;819:5;812:21;930:3;91280:4;921:6;854;912:16;;909:25;906:2;;;947:1;;937:12;906:2;94353:6;91280:4;854:6;850:17;91280:4;888:5;884:16;94330:30;94409:1;94391:16;;;91280:4;94391:16;94384:27;888:5;625:379;-1:-1;;625:379;1179:188;1275:20;;96753:1;96743:12;;96733:2;;96769:1;;96759:12;1374:158;1455:20;;96868:1;96858:12;;96848:2;;96884:1;;96874:12;1718:158;1799:20;;97105:1;97095:12;;97085:2;;97121:1;;97111: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;;;;;93079:46;;97316:35;;97306:2;;97365:1;;97355:12;10397:128;10463:20;;-1:-1;;;;;93405:30;;97563:34;;97553:2;;97611:1;;97601:12;10532:126;10597:20;;93518:4;93507:16;;97684:33;;97674:2;;97731:1;;97721: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;93405:30;;;97563:34;;97553:2;;-1:-1;;97601:12;97553: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;;92147:6;92142:3;92135:19;94353:6;94348:3;92184:4;92179:3;92175:14;94330:30;-1:-1;92184:4;94400:6;92179:3;94391:16;;94384:27;92184:4;91212:9;;95564:2;19295:6;95544:14;95540:28;92179:3;19264:39;;19257:46;;19111:198;;;;;;19317:327;;19452:5;91718:12;92147:6;92142:3;92135:19;19536:52;19581:6;92184:4;92179:3;92175:14;92184:4;19562:5;19558:16;19536:52;;;91212:9;95544:14;-1:-1;;95540:28;19600:39;;;;92184:4;19600:39;;19399:245;-1:-1;;19399:245;47831:253;95967:2;95963:14;;;;-1:-1;;95963:14;17323:74;;48056:2;48047:12;;47947:137;48091:1117;;18026:5;92799:13;92792:21;95864:3;95860:15;17989:3;17982:52;18485:5;91718:12;18596:52;18641:6;48522:1;48517:3;48513:11;18629:4;18622:5;18618:16;18596:52;;;91718:12;;;18660:16;;18596:52;91718:12;48522:1;18660:16;;18629:4;18618:16;;18596:52;;;91718:12;;;18660:16;;18596:52;91718:12;48522:1;18660:16;;18629:4;18618:16;;18596:52;;;95860:15;;;;-1:-1;;;;;;95860:15;;;48522:1;18660:16;;;;;;;47765:54;;;;95860:15;;;;;48958:11;;;47765:54;-1:-1;;95756:15;;-1:-1;;;;;;95756:15;49064:11;;;47394:56;49172:11;;;48419:789;-1:-1;;;;;48419:789;49215:428;;18485:5;91718:12;18596:52;18641:6;18636:3;18629:4;18622:5;18618:16;18596:52;;;91718:12;;;18660:16;;18596:52;91718:12;18660:16;18629:4;18618:16;;18596:52;;;18660:16;;49395:248;-1:-1;;;;49395:248;49650:275;;18485:5;91718:12;18596:52;18641:6;18636:3;18629:4;18622:5;18618:16;18596:52;;;18660:16;;;;;49786:139;-1:-1;;49786:139;49932:520;23501:66;23481:87;;23465:2;23587:12;;18117:37;;;;50415:12;;;50149:303;50459:868;;-1:-1;;;;;95652:15;46812:5;95656:3;95652:15;;46769:3;46762:58;95963:14;;17389:5;95967:2;95963:14;;50844:2;50839:3;50835:12;17323:74;18485:5;91718:12;18596:52;18641:6;50962:12;50839:3;50962:12;18629:4;18622:5;18618:16;18596:52;;;91718:12;;;18660:16;;18596:52;91718:12;50962;18660:16;;18629:4;18618:16;;18596:52;;;92799:13;;92792:21;95864:3;95860:15;50962:12;18660:16;;;;;;;17982:52;-1:-1;;51291:11;;;50735:592;-1:-1;;;;50735:592;51334:1104;;95860:15;;;;47811:5;95864:3;95860:15;;47772:3;47765:54;-1:-1;;;;;95652:15;46812:5;95656:3;95652:15;;51751:1;51746:3;51742:11;46762:58;95963:14;;17389:5;95967:2;95963:14;;51852:12;51746:3;51852:12;17323:74;18485:5;91718:12;18596:52;18641:6;51963:12;51746:3;51963:12;18629:4;18622:5;18618:16;18596:52;;;18669:6;51746:3;18660:16;95860:15;47811:5;95864:3;95860:15;;51963:12;18660:16;;47765:54;95860:15;47811:5;95864:3;95860:15;;52183:11;18660:16;52183:11;47765:54;18485:5;91718:12;18439:52;;18596;18641:6;52289:11;18660:16;52289:11;18629:4;18622:5;18618:16;18596:52;;;18660:16;;;52289:11;18660:16;;51646:792;-1:-1;;;;;;;;;;51646:792;52445:222;-1:-1;;;;;93199:54;;;;17161:45;;52572:2;52557:18;;52543:124;52919:476;-1:-1;;;;;93199:54;;;17161:45;;93199:54;;;;53298:2;53283:18;;17161:45;53381:2;53366:18;;18117:37;;;;53118:2;53103:18;;53089:306;53402:349;-1:-1;;;;;93199:54;;;;17004:58;;53737:2;53722:18;;18117:37;53565:2;53550:18;;53536:215;53758:333;-1:-1;;;;;93199:54;;;17161:45;;93199:54;;54077:2;54062:18;;17161:45;53913:2;53898:18;;53884:207;54454:1280;;54839:3;-1:-1;;;;;93210:42;17199:5;93199:54;17168:3;17161:45;93210:42;17199:5;93199:54;55004:2;54993:9;54989:18;17161:45;;54839:3;55041:2;55030:9;55026:18;55019:48;55081:78;54839:3;54828:9;54824:19;55145:6;55081:78;;;55207:9;55201:4;55197:20;55192:2;55181:9;55177:18;55170:48;55232:78;55305:4;55296:6;55232:78;;;-1:-1;;;;;93405:30;;;55387:3;55372:19;;47142:36;93405:30;;;55469:3;55454:19;;47142:36;-1:-1;;;93405:30;;;;55551:3;55536:19;;47142:36;55635:3;55620:19;;18117:37;55719:3;55704:19;18117:37;;;;55224:86;54810:924;-1:-1;;;;54810:924;56192:548;18117:37;;;93518:4;93507:16;;;;56560:2;56545:18;;47519:35;56643:2;56628:18;;18117:37;56726:2;56711:18;;18117:37;56399:3;56384:19;;56370:370;56747:330;;56904:2;56925:17;56918:47;56979:88;56904:2;56893:9;56889:18;57053:6;57045;56979:88;;57084:433;;57265:2;57286:17;57279:47;57340:88;57265:2;57254:9;57250:18;57414:6;57406;57340:88;;;57332:96;;93518:4;47547:5;93507:16;57503:2;57492:9;57488:18;47519:35;57236:281;;;;;;;57524:310;;57671:2;57692:17;57685:47;57746:78;57671:2;57660:9;57656:18;57810:6;57746:78;;57841:670;;58085:3;58107:17;58100:47;58161:78;58085:3;58074:9;58070:19;58225:6;58161:78;;;-1:-1;;;;;93405:30;;;58316:2;58301:18;;47142:36;93405:30;;;;58406:2;58391:18;;18915:57;58497:2;58482:18;18767:58;58153:86;58056:455;-1:-1;;58056:455;59161:416;59361:2;59375:47;;;20597:2;59346:18;;;92135:19;20633:26;92175:14;;;20613:47;20679:12;;;59332:245;59584:416;59784:2;59798:47;;;20930:2;59769:18;;;92135:19;20966:26;92175:14;;;20946:47;21012:12;;;59755:245;60007:416;60207:2;60221:47;;;21263:2;60192:18;;;92135:19;21299:26;92175:14;;;21279:47;21345:12;;;60178:245;60430:416;60630:2;60644:47;;;21596:2;60615:18;;;92135:19;21632:34;92175:14;;;21612:55;21701:34;21687:12;;;21680:56;-1:-1;;;21756:12;;;21749:36;21804:12;;;60601:245;60853:416;61053:2;61067:47;;;22055:2;61038:18;;;92135:19;22091:34;92175:14;;;22071:55;-1:-1;;;22146:12;;;22139:27;22185:12;;;61024:245;61276:416;61476:2;61490:47;;;22436:2;61461:18;;;92135:19;-1:-1;;;92175:14;;;22452:38;22509:12;;;61447:245;61699:416;61899:2;61913:47;;;22760:2;61884:18;;;92135:19;-1:-1;;;92175:14;;;22776:43;22838:12;;;61870:245;62122:416;62322:2;62336:47;;;23089:2;62307:18;;;92135:19;23125:33;92175:14;;;23105:54;23178:12;;;62293:245;62545:416;62745:2;62759:47;;;23838:2;62730:18;;;92135:19;-1:-1;;;92175:14;;;23854:42;23915:12;;;62716:245;62968:416;63168:2;63182:47;;;24166:2;63153:18;;;92135:19;24202:34;92175:14;;;24182:55;-1:-1;;;24257:12;;;24250:31;24300:12;;;63139:245;63391:416;63591:2;63605:47;;;24551:2;63576:18;;;92135:19;24587:34;92175:14;;;24567:55;-1:-1;;;24642:12;;;24635:36;24690:12;;;63562:245;63814:416;64014:2;64028:47;;;24941:2;63999:18;;;92135:19;24977:34;92175:14;;;24957:55;-1:-1;;;25032:12;;;25025:28;25072:12;;;63985:245;64237:416;64437:2;64451:47;;;25323:2;64422:18;;;92135:19;-1:-1;;;92175:14;;;25339:38;25396:12;;;64408:245;64660:416;64860:2;64874:47;;;25647:2;64845:18;;;92135:19;-1:-1;;;92175:14;;;25663:42;25724:12;;;64831:245;65083:416;65283:2;65297:47;;;25975:2;65268:18;;;92135:19;26011:29;92175:14;;;25991:50;26060:12;;;65254:245;65506:416;65706:2;65720:47;;;26311:2;65691:18;;;92135:19;26347:28;92175:14;;;26327:49;26395:12;;;65677:245;65929:416;66129:2;66143:47;;;26646:2;66114:18;;;92135:19;-1:-1;;;92175:14;;;26662:42;26723:12;;;66100:245;66352:416;66552:2;66566:47;;;26974:2;66537:18;;;92135:19;27010:34;92175:14;;;26990:55;-1:-1;;;27065:12;;;27058:43;27120:12;;;66523:245;66775:416;66975:2;66989:47;;;27371:2;66960:18;;;92135:19;27407:34;92175:14;;;27387:55;-1:-1;;;27462:12;;;27455:27;27501:12;;;66946:245;67198:416;67398:2;67412:47;;;27752:2;67383:18;;;92135:19;-1:-1;;;92175:14;;;27768:36;27823:12;;;67369:245;67621:416;67821:2;67835:47;;;28074:2;67806:18;;;92135:19;28110:34;92175:14;;;28090:55;-1:-1;;;28165:12;;;28158:26;28203:12;;;67792:245;68044:416;68244:2;68258:47;;;28454:2;68229:18;;;92135:19;28490:34;92175:14;;;28470:55;-1:-1;;;28545:12;;;28538:41;28598:12;;;68215:245;68467:416;68667:2;68681:47;;;28849:2;68652:18;;;92135:19;28885:34;92175:14;;;28865:55;-1:-1;;;28940:12;;;28933:26;28978:12;;;68638:245;68890:416;69090:2;69104:47;;;29229:2;69075:18;;;92135:19;29265:34;92175:14;;;29245:55;-1:-1;;;29320:12;;;29313:28;29360:12;;;69061:245;69313:416;69513:2;69527:47;;;29611:2;69498:18;;;92135:19;29647:34;92175:14;;;29627:55;-1:-1;;;29702:12;;;29695:31;29745:12;;;69484:245;69736:416;69936:2;69950:47;;;29996:2;69921:18;;;92135:19;-1:-1;;;92175:14;;;30012:42;30073:12;;;69907:245;70159:416;70359:2;70373:47;;;30324:2;70344:18;;;92135:19;30360:34;92175:14;;;30340:55;-1:-1;;;30415:12;;;30408:25;30452:12;;;70330:245;70582:416;70782:2;70796:47;;;30703:2;70767:18;;;92135:19;30739:27;92175:14;;;30719:48;30786:12;;;70753:245;71005:416;71205:2;71219:47;;;71190:18;;;92135:19;31073:34;92175:14;;;31053:55;31127:12;;;71176:245;71428:416;71628:2;71642:47;;;31378:2;71613:18;;;92135:19;-1:-1;;;92175:14;;;31394:44;31457:12;;;71599:245;71851:416;72051:2;72065:47;;;31708:2;72036:18;;;92135:19;31744:26;92175:14;;;31724:47;31790:12;;;72022:245;72274:416;72474:2;72488:47;;;32041:2;72459:18;;;92135:19;32077:34;92175:14;;;32057:55;-1:-1;;;32132:12;;;32125:31;32175:12;;;72445:245;72697:416;72897:2;72911:47;;;32426:2;72882:18;;;92135:19;-1:-1;;;92175:14;;;32442:44;32505:12;;;72868:245;73120:416;73320:2;73334:47;;;32756:2;73305:18;;;92135:19;32792:34;92175:14;;;32772:55;-1:-1;;;32847:12;;;32840:26;32885:12;;;73291:245;73543:416;73743:2;73757:47;;;33136:2;73728:18;;;92135:19;33172:26;92175:14;;;33152:47;33218:12;;;73714:245;73966:416;74166:2;74180:47;;;33469:2;74151:18;;;92135:19;33505:34;92175:14;;;33485:55;-1:-1;;;33560:12;;;33553:28;33600:12;;;74137:245;74389:416;74589:2;74603:47;;;33851:2;74574:18;;;92135:19;33887:34;92175:14;;;33867:55;-1:-1;;;33942:12;;;33935:27;33981:12;;;74560:245;74812:416;75012:2;75026:47;;;34232:2;74997:18;;;92135:19;-1:-1;;;92175:14;;;34248:39;34306:12;;;74983:245;75235:416;75435:2;75449:47;;;34557:2;75420:18;;;92135:19;34593:34;92175:14;;;34573:55;-1:-1;;;34648:12;;;34641:25;34685:12;;;75406:245;75658:416;75858:2;75872:47;;;34936:2;75843:18;;;92135:19;34972:26;92175:14;;;34952:47;35018:12;;;75829:245;76081:416;76281:2;76295:47;;;35269:2;76266:18;;;92135:19;35305:26;92175:14;;;35285:47;35351:12;;;76252:245;76504:416;76704:2;76718:47;;;35602:2;76689:18;;;92135:19;35638:31;92175:14;;;35618:52;35689:12;;;76675:245;76927:416;77127:2;77141:47;;;35940:2;77112:18;;;92135:19;-1:-1;;;92175:14;;;35956:43;36018:12;;;77098:245;77350:416;77550:2;77564:47;;;36269:2;77535:18;;;92135:19;36305:32;92175:14;;;36285:53;36357:12;;;77521:245;77773:416;77973:2;77987:47;;;36608:2;77958:18;;;92135:19;36644:24;92175:14;;;36624:45;36688:12;;;77944:245;78196:416;78396:2;78410:47;;;36939:2;78381:18;;;92135:19;36975:25;92175:14;;;36955:46;37020:12;;;78367:245;78619:416;78819:2;78833:47;;;37271:2;78804:18;;;92135:19;37307:32;92175:14;;;37287:53;37359:12;;;78790:245;79042:416;79242:2;79256:47;;;37610:2;79227:18;;;92135:19;-1:-1;;;92175:14;;;37626:42;37687:12;;;79213:245;79465:416;79665:2;79679:47;;;37938:2;79650:18;;;92135:19;37974:34;92175:14;;;37954:55;-1:-1;;;38029:12;;;38022:25;38066:12;;;79636:245;79888:416;80088:2;80102:47;;;38317:2;80073:18;;;92135:19;38353:34;92175:14;;;38333:55;-1:-1;;;38408:12;;;38401:28;38448:12;;;80059:245;80311:416;80511:2;80525:47;;;38699:2;80496:18;;;92135:19;-1:-1;;;92175:14;;;38715:43;38777:12;;;80482:245;80734:416;80934:2;80948:47;;;39028:2;80919:18;;;92135:19;39064:34;92175:14;;;39044:55;-1:-1;;;39119:12;;;39112:31;39162:12;;;80905:245;81157:416;81357:2;81371:47;;;39413:2;81342:18;;;92135:19;39449:34;92175:14;;;39429:55;-1:-1;;;39504:12;;;39497:44;39560:12;;;81328:245;81580:416;81780:2;81794:47;;;39811:2;81765:18;;;92135:19;-1:-1;;;92175:14;;;39827:44;39890:12;;;81751:245;82003:416;82203:2;82217:47;;;40141:2;82188:18;;;92135:19;40177:34;92175:14;;;40157:55;-1:-1;;;40232:12;;;40225:40;40284:12;;;82174:245;82426:416;82626:2;82640:47;;;40535:2;82611:18;;;92135:19;40571:34;92175:14;;;40551:55;-1:-1;;;40626:12;;;40619:32;40670:12;;;82597:245;82849:416;83049:2;83063:47;;;40921:2;83034:18;;;92135:19;40957:33;92175:14;;;40937:54;41010:12;;;83020:245;83272:416;83472:2;83486:47;;;41261:2;83457:18;;;92135:19;-1:-1;;;92175:14;;;41277:43;41339:12;;;83443:245;83695:416;83895:2;83909:47;;;41590:2;83880:18;;;92135:19;-1:-1;;;92175:14;;;41606:40;41665:12;;;83866:245;84118:416;84318:2;84332:47;;;41916:2;84303:18;;;92135:19;41952:24;92175:14;;;41932:45;41996:12;;;84289:245;84541:416;84741:2;84755:47;;;42247:2;84726:18;;;92135:19;42283:34;92175:14;;;42263:55;-1:-1;;;42338:12;;;42331:35;42385:12;;;84712:245;84964:416;85164:2;85178:47;;;42636:2;85149:18;;;92135:19;42672:34;92175:14;;;42652:55;42741:34;42727:12;;;42720:56;-1:-1;;;42796:12;;;42789:32;42840:12;;;85135:245;85387:416;85587:2;85601:47;;;43091:2;85572:18;;;92135:19;-1:-1;;;92175:14;;;43107:42;43168:12;;;85558:245;85810:416;86010:2;86024:47;;;43419:2;85995:18;;;92135:19;43455:27;92175:14;;;43435:48;43502:12;;;85981:245;86233:416;86433:2;86447:47;;;43753:2;86418:18;;;92135:19;43789:34;92175:14;;;43769:55;-1:-1;;;43844:12;;;43837:32;43888:12;;;86404:245;86656:416;86856:2;86870:47;;;44139:2;86841:18;;;92135:19;-1:-1;;;92175:14;;;44155:36;44210:12;;;86827:245;87079:416;87279:2;87293:47;;;44461:2;87264:18;;;92135:19;-1:-1;;;92175:14;;;44477:43;44539:12;;;87250:245;87502:416;87702:2;87716:47;;;44790:2;87687:18;;;92135:19;44826:32;92175:14;;;44806:53;44878:12;;;87673:245;87925:416;88125:2;88139:47;;;45129:2;88110:18;;;92135:19;45165:34;92175:14;;;45145:55;-1:-1;;;45220:12;;;45213:31;45263:12;;;88096:245;88348:362;;88521:2;88542:17;88535:47;45561:16;45555:23;92799:13;92792:21;88521:2;88510:9;88506:18;17853:34;-1:-1;;;;;88521:2;45721:5;45717:16;45711:23;93199:54;45788:14;88510:9;45788:14;17161:45;45788:14;45877:5;45873:16;45867:23;45487:4;45910:14;88510:9;45910:14;45903:38;45956:73;45478:14;88510:9;45478:14;46010:12;45956:73;;;93518:4;45910:14;46111:5;46107:16;46101:23;93507:16;46174:14;88510:9;46174:14;47519:35;46174:14;46268:5;46264:16;46258:23;92799:13;92792:21;46329:14;88510:9;46329:14;17853:34;-1:-1;;;;;46329:14;46434:5;46430:16;46424:23;93405:30;45487:4;88510:9;46499:14;47142:36;88588:112;;;;;88492:218;;;;;88717:442;-1:-1;;;;;93079:46;;;;46624:37;;-1:-1;;;;;93405:30;;;;89062:2;89047:18;;18915:57;89145:2;89130:18;;18117:37;88899:2;88884:18;;88870:289;89166:222;18117:37;;;89293:2;89278:18;;89264:124;89395:333;18117:37;;;89714:2;89699:18;;18117:37;89550:2;89535:18;;89521:207;89735:218;-1:-1;;;;;93405:30;;;;47142:36;;89860:2;89845:18;;89831:122;89960:744;;-1:-1;;;;;93416:18;47171:5;93405:30;47149:3;47142:36;90213:3;90330:2;90319:9;90315:18;90308:48;90370:78;90213:3;90202:9;90198:19;90434:6;90370:78;;;93405:30;;;90525:2;90510:18;;47142:36;93405:30;;;;90606:2;90591:18;;47142:36;90689:3;90674:19;18117:37;-1:-1;90362:86;90184:520;-1:-1;;90184:520;90711:256;90773:2;90767:9;90799:17;;;-1:-1;;;;;90859:34;;90895:22;;;90856:62;90853:2;;;90931:1;;90921:12;90853:2;90773;90940:22;90751:216;;-1:-1;90751:216;94426:268;94491:1;94498:101;94512:6;94509:1;94506:13;94498:101;;;94579:11;;;94573:18;94560:11;;;94553:39;94534:2;94527:10;94498:101;;;94614:6;94611:1;94608:13;94605:2;;;-1:-1;;94491:1;94661:16;;94654:27;94475:219;95995:117;-1:-1;;;;;96082:5;93199:54;96057:5;96054:35;96044:2;;96103:1;;96093:12;96044:2;96038:74;

Swarm Source

ipfs://47320e0b0b3f8ba0d6c8567653417f0196b8c76c378d33d1ec502161d92adbc6
Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading