Contract 0x8C788aA08A98002413F6350C29c017aefb2c08C7

 
Txn Hash
Block
From
To
Value [Txn Fee]
0xadede5ab8692fd167db1d577cc594cc733b74118fb96ec8adddf82313704843a72872782021-05-10 3:51:294 hrs 12 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000481505
0xb5a60b1fa1227660a7b318ac3e893976abe3b4470b6dbc71f7afd1ba72db408072865622021-05-10 3:15:214 hrs 48 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.001090445
0x6205c6ce7bf6686a2ed5780296c98060e15ce9ebbf1ba7f77a3ce3225ba6301e72833972021-05-10 0:36:307 hrs 27 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.001144175
0x937c123f16ef956fa3a27d9f8d09342149f893f4dac3417e7e6af8d75e561c7072833892021-05-10 0:36:067 hrs 27 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.001152685
0x0fb13111cc0942be87eb2f0afda15f66983e7dfea00fab28853c00924644159072833652021-05-10 0:34:547 hrs 29 mins ago0x58029388cd700a341de357429fb241ac60547000 IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000463875
0x49e1c41086eb5ee64f343d5bc10962fffa0b0c641e9f7c7178a0a8690e6e6e2b72799952021-05-09 21:41:3810 hrs 22 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000386175
0xd726362855bce7de3cbb71864e9a4151f4337314cbd836fabfa0eb844647e12372782082021-05-09 20:10:5311 hrs 53 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00038577
0x31a8bc75d9067ece84969c08c716d8c4e9df8803f4da964379270804b1f4587672734062021-05-09 15:51:0216 hrs 13 mins ago0x7cc1e1b3aab38df00ae837c9c439580da257db35 IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000546835
0xe35c94b83de12d5664d0930c4d041949e25e7093c8dcf207e973e68197315e1e72733832021-05-09 15:49:3616 hrs 14 mins ago0x7cc1e1b3aab38df00ae837c9c439580da257db35 IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000645
0x8b526f2e6e6bdee1e64a4ef6e15eb259ea92807a814564e422ba28d299bf588e72523832021-05-08 21:31:511 day 10 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000598755
0x51c0c0b89de1755b690b76c546b4c0aa6fd1099ab9361d0fd5deaf98b30e619f72523242021-05-08 21:28:501 day 10 hrs ago0xd7638141777523a43f4207a3140e6df9f2a9431a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000546835
0x73c4f1f62ac421cd165a9f789a283c89cb66ce89cdbd78de2d7b4e9498f3289572523142021-05-08 21:28:201 day 10 hrs ago0xd7638141777523a43f4207a3140e6df9f2a9431a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000645
0x403235d86248c1735d07f4baa6ed89959d40c6a333391a741760da0dc73430b372504142021-05-08 19:48:381 day 12 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00057097
0xf335b8e5e946c7b8c18d023241f0c98599dfc3e6cbfb9a887a7040e7c6a2f49272490542021-05-08 18:37:511 day 13 hrs ago0x547aaa1bf305120abb5c08f2697de2a4cbae46b1 IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000645
0x918d5f289432b60d1a3345a023a8c22b1cc120d47c1ed8143ab3ba859ab2bc9272476612021-05-08 17:22:311 day 14 hrs ago0xc99793ab5abb268ab1d2577351975bdba670c5bf IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70.09 BNB0.00033578
0xf21550c4a6385a741617aa132bb29f0f9372bf6daf91379c99a87d6b775a1f1572386082021-05-08 9:26:431 day 22 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00038567
0x05b0ca86e40ae92ef7b6d5e5ec8fdbede4bb1f7e4defa9b81eb54ebffb2d601572367432021-05-08 7:52:582 days 11 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00038565
0x9e364472670ad054fb82946a5b10067bc16d89f0ba1e1c62ef99bc8c56bc909c72358022021-05-08 7:05:382 days 58 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000460935
0x9985a1546794e8fca8d4cbbc265068756570024a31c9f8d5387e411a583b69f272357912021-05-08 7:05:052 days 58 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000479445
0x6600f0e31f3bf859f3b6b4069d258c6f572a7e9ae49ea0893ff7bf6f80171efe72357862021-05-08 7:04:502 days 59 mins ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00062957
0x6093134f47046de5a2f09ca420edf007dcad1b5cde4d87c505d2789118c1b28e72355252021-05-08 6:51:472 days 1 hr ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00038577
0xa6bc725a128d5ebc6ad8da2d5883c22dc9b44ad0a9494c235b4250bcc790fe5c72340132021-05-08 5:36:112 days 2 hrs ago0xe3387afa8046fb480ec5bc4e37ccf70298bad9d4 IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000645
0x4ccd594ec2586c4fa8d861a5363b2a7335f6e04036b7fcb7f9307b59f354234072332112021-05-08 4:52:312 days 3 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00038577
0x1367adfed8a246b9e0023c26512735519557a4e80c0bf776e9dd8d834e7ff5db72276202021-05-08 0:08:502 days 7 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.000598755
0x5680f7119ae87e0003eaca3186d32413d779c6e02b003663af490d0316889e3672204102021-05-07 18:00:422 days 14 hrs ago0xc0c0ac44399f496bb67d022af8411f7da4b02b9a IN  0x8c788aa08a98002413f6350c29c017aefb2c08c70 BNB0.00067402
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x918d5f289432b60d1a3345a023a8c22b1cc120d47c1ed8143ab3ba859ab2bc9272476612021-05-08 17:22:311 day 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.09 BNB
0x2840bbc910a73bad18db85a7b4b4d4dbbe5b34abfd752625f9df4116ca2d772572197052021-05-07 17:24:532 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.05 BNB
0x3cedc79677cb4227305fdfbbeb6d4c6a7f8638ca5e38d3e2b05e8ac554daadbf72195832021-05-07 17:18:472 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.0022 BNB
0x4b338fe9be092a0cce91577e243e6a3b86c1202705b44dbb89cb9ebde603fa5772195772021-05-07 17:18:292 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.0022 BNB
0x9cad90503ed3dd1b8460d8df791a9096dd242e5f1936a1fef74ab2de486da79772174192021-05-07 15:26:392 days 16 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.3 BNB
0x4f36063236afe33d98c4a8dfea692eae0d8c55bb7334264fb17fb1879f74c2e471919112021-05-06 17:50:073 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.06 BNB
0x5b4947b0686e8ebc203125f157fe917067aa4eb086967f72c8c1045700c5cd0571583322021-05-05 12:46:564 days 19 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.00423295 BNB
0x03cd4344918e6cb2689c7c13617725ae99f947e303b358bcfd137a9b3c0855ab71103592021-05-03 20:31:016 days 11 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.12441687 BNB
0x1bc6f65ee9fbad2d7b81b2585c98d1d4dab57c98b35f39d053331897f885131771092532021-05-03 19:35:336 days 12 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.0051178 BNB
0x9caee29ea9a67f48fb3b1e003ce86c7e17e247311033399fec71d1e56e9c260470486762021-05-01 16:30:488 days 15 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.1 BNB
0x77604ddc5a88614fcddb809fd8644b6f6ffddcdcdd4d5c64acec3b9eddeb5a9d70326002021-05-01 3:01:559 days 5 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.0341191 BNB
0x237ff77065f0b523121ec95ffe64e0bfac0738f43af6f4a0b5783209504352c370249612021-04-30 20:39:479 days 11 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.40360142 BNB
0x46b7d591085ccced271336d7fbab497e011c540a9e78de9511580bbd9b8f4e3e70111112021-04-30 8:44:169 days 23 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.00829788 BNB
0x41637f8f0315290d4d3f83033fbb0e66cdc9a8fa6191dd9ac6da335ba1f17f7770044392021-04-30 3:04:5810 days 4 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.59 BNB
0xbf0bb50109c094ecc31d8d00e71cddc29f60feb82c1eb774d5df050a1a6958d069743122021-04-29 1:46:5711 days 6 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.01077536 BNB
0x329c2541dcb6ff44c089549bdfba92d0899b09e541d9be3ac6abdd1846d3273669717702021-04-28 23:39:4511 days 8 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.18088935 BNB
0xdadc7285f1590e7b5812c6357f3061ecccb05e34fa2cf874b4542448a405a89269645792021-04-28 17:38:2611 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212835.28 BNB
0x100a6af65242c20ffe878f00fd254d2d63d44cdd48006d7b249ea3869d737a9d69431632021-04-27 23:43:0512 days 8 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.00215 BNB
0x102e227bb7304511c4074832ebd2e9ce016fc56edfdccf28294859ac35909f3269431602021-04-27 23:42:5612 days 8 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.0029 BNB
0xdfe17420772a4fd25a805ac1b4e219957f3f355fa275b68327f5ca9b037d007669361302021-04-27 17:49:1212 days 14 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.6 BNB
0x59b3843707ddae5aa9ee485d510129d16fbea1a098eaaff6700ee6a89f8a391c69280712021-04-27 10:57:0612 days 21 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.1 BNB
0x2b72c95791527b2a5667305bfc382ccc56fb847bb0536dc8d3d72ba802dc25a069275482021-04-27 10:30:5712 days 21 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212831.9465429 BNB
0xf2504111560bbefad2771738bcc4c52265c012f3ec39241ec21e773ce9bb6ec669272232021-04-27 10:14:4212 days 21 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212831.679 BNB
0x460fefab2a9cc18afacb4acb4d75343059186143384b5a95bd64084c605f874069271812021-04-27 10:12:3612 days 21 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.2619389 BNB
0xe4f98e8f03d3706905a2b36dabf7dd9b1b79876bf1bff9e79ef4f04c87dcb4c569268242021-04-27 9:54:4512 days 22 hrs ago 0x8c788aa08a98002413f6350c29c017aefb2c08c7 0xc88c5b8951dd877afe5558a48450d0ef181212830.26 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
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.