Polygon Sponsored slots available. Book your slot here!
Contract Overview
Balance:
0.00000006 MATIC
MATIC Value:
Less Than $0.01 (@ $0.66/MATIC)
My Name Tag:
Not Available, login to update
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0x295c9200b729aa52d98bc6fccc32d2245ea05607d0344b4d2da04ce3c41bd34c | 0x611a7761 | 21255741 | 194 days 6 hrs ago | 0x8af97264482b59c7aa11010907710dee6d8d8c6c | IN | Create: Withdrawing | 0.00000006 MATIC | 0.04549389 |
[ Download CSV Export ]
Contract Name:
Withdrawing
Compiler Version
v0.8.4+commit.c7e474f2
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @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 on 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"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 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); } } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { Address } from './Address.sol'; import { Asset } from './Structs.sol'; import { AssetTransfers } from './AssetTransfers.sol'; import { AssetUnitConversions } from './AssetUnitConversions.sol'; import { BalanceTracking } from './BalanceTracking.sol'; import { IERC20 } from './Interfaces.sol'; /** * @notice Library helper functions for reading from a registry of asset descriptors indexed by address and symbol */ library AssetRegistry { struct Storage { mapping(address => 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 => Asset[]) assetsBySymbol; // Blockchain-specific native asset symbol string nativeAssetSymbol; } // Admin // function registerToken( AssetRegistry.Storage storage self, IERC20 tokenAddress, string calldata symbol, uint8 decimals ) external { require(decimals <= 32, 'Token cannot have more than 32 decimals'); require( tokenAddress != IERC20(address(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)] = Asset({ exists: true, assetAddress: address(tokenAddress), symbol: symbol, decimals: decimals, isConfirmed: false, confirmedTimestampInMs: 0 }); } function confirmTokenRegistration( AssetRegistry.Storage storage self, IERC20 tokenAddress, string calldata symbol, uint8 decimals ) external { 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( AssetRegistry.Storage storage self, IERC20 tokenAddress, string calldata symbol ) external { Asset memory asset = self.assetsByAddress[address(tokenAddress)]; require( asset.exists && asset.isConfirmed, 'Registration of token not finalized' ); require( !isStringEqual(symbol, self.nativeAssetSymbol), 'Symbol reserved for native asset' ); // This will prevent swapping assets for previously existing orders uint64 msInOneSecond = 1000; asset.confirmedTimestampInMs = uint64(block.timestamp * msInOneSecond); self.assetsBySymbol[symbol].push(asset); } function skim(address tokenAddress, address feeWallet) external { require(Address.isContract(tokenAddress), 'Invalid token address'); uint256 balance = IERC20(tokenAddress).balanceOf(address(this)); AssetTransfers.transferTo(payable(feeWallet), tokenAddress, balance); } // Accessors // function loadBalanceInAssetUnitsByAddress( AssetRegistry.Storage storage self, address wallet, address assetAddress, BalanceTracking.Storage storage balanceTracking ) external view returns (uint256) { require(wallet != address(0x0), 'Invalid wallet address'); Asset memory asset = loadAssetByAddress(self, assetAddress); return AssetUnitConversions.pipsToAssetUnits( loadBalanceInPipsFromMigrationSourceIfNeeded( wallet, assetAddress, balanceTracking ), asset.decimals ); } function loadBalanceInAssetUnitsBySymbol( AssetRegistry.Storage storage self, address wallet, string calldata assetSymbol, BalanceTracking.Storage storage balanceTracking ) external view returns (uint256) { require(wallet != address(0x0), 'Invalid wallet address'); Asset memory asset = loadAssetBySymbol(self, assetSymbol, getCurrentTimestampInMs()); return AssetUnitConversions.pipsToAssetUnits( loadBalanceInPipsFromMigrationSourceIfNeeded( wallet, asset.assetAddress, balanceTracking ), asset.decimals ); } function loadBalanceInPipsByAddress( address wallet, address assetAddress, BalanceTracking.Storage storage balanceTracking ) external view returns (uint64) { require(wallet != address(0x0), 'Invalid wallet address'); return loadBalanceInPipsFromMigrationSourceIfNeeded( wallet, assetAddress, balanceTracking ); } function loadBalanceInPipsBySymbol( Storage storage self, address wallet, string calldata assetSymbol, BalanceTracking.Storage storage balanceTracking ) external view returns (uint64) { require(wallet != address(0x0), 'Invalid wallet address'); address assetAddress = loadAssetBySymbol(self, assetSymbol, getCurrentTimestampInMs()) .assetAddress; return loadBalanceInPipsFromMigrationSourceIfNeeded( wallet, assetAddress, balanceTracking ); } function loadBalanceInPipsFromMigrationSourceIfNeeded( address wallet, address assetAddress, BalanceTracking.Storage storage balanceTracking ) private view returns (uint64) { BalanceTracking.Balance memory balance = balanceTracking.balancesByWalletAssetPair[wallet][assetAddress]; if ( !balance.isMigrated && address(balanceTracking.migrationSource) != address(0x0) ) { return balanceTracking.migrationSource.loadBalanceInPipsByAddress( wallet, assetAddress ); } return balance.balanceInPips; } /** * @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 (Asset memory) { if (assetAddress == address(0x0)) { return getEthAsset(self.nativeAssetSymbol); } 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 (Asset memory) { if (isStringEqual(self.nativeAssetSymbol, symbol)) { return getEthAsset(self.nativeAssetSymbol); } 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; } // Util // function getCurrentTimestampInMs() internal view returns (uint64) { uint64 msInOneSecond = 1000; return uint64(block.timestamp) * msInOneSecond; } /** * @dev ETH is modeled as an always-confirmed Asset struct for programmatic consistency */ function getEthAsset(string memory nativeAssetSymbol) private pure returns (Asset memory) { return Asset(true, address(0x0), nativeAssetSymbol, 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)); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; 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 { /** * @dev Transfers tokens from a wallet into a contract during deposits. `wallet` must already * have called `approve` on the token contract for at least `tokenQuantity`. Note this only * applies to tokens since ETH is sent in the deposit transaction via `msg.value` */ function transferFrom( address wallet, IERC20 tokenAddress, address to, uint256 quantityInAssetUnits ) internal { uint256 balanceBefore = tokenAddress.balanceOf(to); // Because we check for the expected balance change we can safely ignore the return value of transferFrom tokenAddress.transferFrom(wallet, to, quantityInAssetUnits); uint256 balanceAfter = tokenAddress.balanceOf(to); require( balanceAfter - balanceBefore == quantityInAssetUnits, 'Token contract returned transferFrom success without expected balance change' ); } /** * @dev Transfers ETH or token assets from a contract to a wallet when withdrawing or removing liquidity */ function transferTo( address payable walletOrContract, address asset, uint256 quantityInAssetUnits ) internal { if (asset == address(0x0)) { require( walletOrContract.send(quantityInAssetUnits), 'ETH transfer failed' ); } else { uint256 balanceBefore = IERC20(asset).balanceOf(walletOrContract); // Because we check for the expected balance change we can safely ignore the return value of transfer IERC20(asset).transfer(walletOrContract, quantityInAssetUnits); uint256 balanceAfter = IERC20(asset).balanceOf(walletOrContract); require( balanceAfter - balanceBefore == quantityInAssetUnits, 'Token contract returned transfer success without expected balance change' ); } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; /** * @notice Library helpers for converting asset quantities between asset units and pips */ library AssetUnitConversions { 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) * (uint256(10)**(assetDecimals - 8)); } return uint256(quantityInPips) / (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 / (uint256(10)**(assetDecimals - 8)); } else { quantityInPips = quantityInAssetUnits * (uint256(10)**(8 - assetDecimals)); } require(quantityInPips < 2**64, 'Pip quantity overflows uint64'); return uint64(quantityInPips); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { AssetRegistry } from './AssetRegistry.sol'; import { AssetUnitConversions } from './AssetUnitConversions.sol'; import { Constants } from './Constants.sol'; import { OrderSide } from './Enums.sol'; import { PoolTradeHelpers } from './PoolTradeHelpers.sol'; import { Asset, HybridTrade, LiquidityAddition, LiquidityChangeExecution, LiquidityRemoval, Order, OrderBookTrade, PoolTrade, Withdrawal } from './Structs.sol'; import { IExchange, ILiquidityProviderToken } from './Interfaces.sol'; library BalanceTracking { using AssetRegistry for AssetRegistry.Storage; using PoolTradeHelpers for PoolTrade; struct Balance { bool isMigrated; uint64 balanceInPips; } struct Storage { mapping(address => mapping(address => Balance)) balancesByWalletAssetPair; // Predecessor Exchange contract from which to lazily migrate balances IExchange migrationSource; } // Depositing // function updateForDeposit( Storage storage self, address wallet, address assetAddress, uint64 quantityInPips ) internal returns (uint64 newBalanceInPips) { Balance storage balance = loadBalanceAndMigrateIfNeeded(self, wallet, assetAddress); balance.balanceInPips += quantityInPips; return balance.balanceInPips; } // Trading // /** * @dev Updates buyer, seller, and fee wallet balances for both assets in trade pair according to * trade parameters */ function updateForOrderBookTrade( Storage storage self, Order memory buy, Order memory sell, OrderBookTrade memory trade, address feeWallet ) internal { Balance storage balance; // Seller gives base asset including fees balance = loadBalanceAndMigrateIfNeeded( self, sell.walletAddress, trade.baseAssetAddress ); balance.balanceInPips -= trade.grossBaseQuantityInPips; // Buyer receives base asset minus fees balance = loadBalanceAndMigrateIfNeeded( self, buy.walletAddress, trade.baseAssetAddress ); balance.balanceInPips += trade.netBaseQuantityInPips; // Buyer gives quote asset including fees balance = loadBalanceAndMigrateIfNeeded( self, buy.walletAddress, trade.quoteAssetAddress ); balance.balanceInPips -= trade.grossQuoteQuantityInPips; // Seller receives quote asset minus fees balance = loadBalanceAndMigrateIfNeeded( self, sell.walletAddress, trade.quoteAssetAddress ); balance.balanceInPips += trade.netQuoteQuantityInPips; // Maker fee to fee wallet balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, trade.makerFeeAssetAddress ); balance.balanceInPips += trade.makerFeeQuantityInPips; // Taker fee to fee wallet balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, trade.takerFeeAssetAddress ); balance.balanceInPips += trade.takerFeeQuantityInPips; } function updateForPoolTrade( Storage storage self, Order memory order, PoolTrade memory poolTrade, address feeWallet ) internal { Balance storage balance; // Debit from order wallet balance = loadBalanceAndMigrateIfNeeded( self, order.walletAddress, poolTrade.getOrderDebitAssetAddress(order.side) ); balance.balanceInPips -= poolTrade.getOrderDebitQuantityInPips(order.side); // Credit to order wallet balance = loadBalanceAndMigrateIfNeeded( self, order.walletAddress, poolTrade.getOrderCreditAssetAddress(order.side) ); balance.balanceInPips += poolTrade.calculateOrderCreditQuantityInPips( order.side ); // Fee wallet receives protocol fee from asset debited from order wallet balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, poolTrade.getOrderDebitAssetAddress(order.side) ); balance.balanceInPips += poolTrade.takerProtocolFeeQuantityInPips; // Fee wallet receives gas fee from asset credited to order wallet balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, poolTrade.getOrderCreditAssetAddress(order.side) ); balance.balanceInPips += poolTrade.takerGasFeeQuantityInPips; // Liquidity pool reserves are updated in LiquidityPoolRegistry } function updateForHybridTradeFees( Storage storage self, HybridTrade memory hybridTrade, address takerWallet, address feeWallet ) internal { Balance storage balance; balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, hybridTrade.orderBookTrade.takerFeeAssetAddress ); balance.balanceInPips += hybridTrade.takerGasFeeQuantityInPips; balance = loadBalanceAndMigrateIfNeeded( self, takerWallet, hybridTrade.orderBookTrade.takerFeeAssetAddress ); balance.balanceInPips -= hybridTrade.takerGasFeeQuantityInPips + hybridTrade.poolTrade.takerPriceCorrectionFeeQuantityInPips; // Liquidity pool reserves are updated in LiquidityPoolRegistry } // Withdrawing // function updateForWithdrawal( Storage storage self, Withdrawal memory withdrawal, address assetAddress, address feeWallet ) internal returns (uint64 newExchangeBalanceInPips) { Balance storage balance; balance = loadBalanceAndMigrateIfNeeded( self, withdrawal.walletAddress, assetAddress ); // Reverts if balance is overdrawn balance.balanceInPips -= withdrawal.grossQuantityInPips; newExchangeBalanceInPips = balance.balanceInPips; if (withdrawal.gasFeeInPips > 0) { balance = loadBalanceAndMigrateIfNeeded(self, feeWallet, assetAddress); balance.balanceInPips += withdrawal.gasFeeInPips; } } // Wallet exits // function updateForExit( Storage storage self, address wallet, address assetAddress ) internal returns (uint64 previousExchangeBalanceInPips) { Balance storage balance; balance = loadBalanceAndMigrateIfNeeded(self, wallet, assetAddress); previousExchangeBalanceInPips = balance.balanceInPips; require(previousExchangeBalanceInPips > 0, 'No balance for asset'); balance.balanceInPips = 0; } // Liquidity pools // function updateForAddLiquidity( Storage storage self, LiquidityAddition memory addition, LiquidityChangeExecution memory execution, address feeWallet, address custodianAddress, ILiquidityProviderToken liquidityProviderToken ) internal returns (uint64 outputLiquidityInPips) { // Base gross debit Balance storage balance = loadBalanceAndMigrateIfNeeded( self, addition.wallet, execution.baseAssetAddress ); balance.balanceInPips -= execution.grossBaseQuantityInPips; // Base fee credit balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, execution.baseAssetAddress ); balance.balanceInPips += execution.grossBaseQuantityInPips - execution.netBaseQuantityInPips; // Quote gross debit balance = loadBalanceAndMigrateIfNeeded( self, addition.wallet, execution.quoteAssetAddress ); balance.balanceInPips -= execution.grossQuoteQuantityInPips; // Quote fee credit balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, execution.quoteAssetAddress ); balance.balanceInPips += execution.grossQuoteQuantityInPips - execution.netQuoteQuantityInPips; // Only add output assets to wallet's balances in the Exchange if Custodian is target if (addition.to == custodianAddress) { balance = loadBalanceAndMigrateIfNeeded( self, addition.wallet, address(liquidityProviderToken) ); balance.balanceInPips += execution.liquidityInPips; } else { outputLiquidityInPips = execution.liquidityInPips; } } function updateForRemoveLiquidity( Storage storage self, LiquidityRemoval memory removal, LiquidityChangeExecution memory execution, address feeWallet, address custodianAddress, ILiquidityProviderToken liquidityProviderToken ) internal returns ( uint64 outputBaseAssetQuantityInPips, uint64 outputQuoteAssetQuantityInPips ) { Balance storage balance; // Base asset updates { // Only add output assets to wallet's balances in the Exchange if Custodian is target if (removal.to == custodianAddress) { // Base net credit balance = loadBalanceAndMigrateIfNeeded( self, removal.wallet, execution.baseAssetAddress ); balance.balanceInPips += execution.netBaseQuantityInPips; } else { outputBaseAssetQuantityInPips = execution.netBaseQuantityInPips; } // Base fee credit balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, execution.baseAssetAddress ); balance.balanceInPips += execution.grossBaseQuantityInPips - execution.netBaseQuantityInPips; } // Quote asset updates { // Only add output assets to wallet's balances in the Exchange if Custodian is target if (removal.to == custodianAddress) { // Quote net credit balance = loadBalanceAndMigrateIfNeeded( self, removal.wallet, execution.quoteAssetAddress ); balance.balanceInPips += execution.netQuoteQuantityInPips; } else { outputQuoteAssetQuantityInPips = execution.netQuoteQuantityInPips; } // Quote fee credit balance = loadBalanceAndMigrateIfNeeded( self, feeWallet, execution.quoteAssetAddress ); balance.balanceInPips += execution.grossQuoteQuantityInPips - execution.netQuoteQuantityInPips; } // Pair token burn { balance = loadBalanceAndMigrateIfNeeded( self, removal.wallet, address(liquidityProviderToken) ); balance.balanceInPips -= execution.liquidityInPips; } } // Helpers // function loadBalanceAndMigrateIfNeeded( Storage storage self, address wallet, address assetAddress ) private returns (Balance storage) { Balance storage balance = self.balancesByWalletAssetPair[wallet][assetAddress]; if (!balance.isMigrated && address(self.migrationSource) != address(0x0)) { balance.balanceInPips = self.migrationSource.loadBalanceInPipsByAddress( wallet, assetAddress ); balance.isMigrated = true; } return balance; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; /** * @dev See GOVERNANCE.md for descriptions of fixed parameters and fees */ library Constants { // 100 basis points/percent * 100 percent/total uint64 public constant basisPointsInTotal = 100 * 100; uint64 public constant depositIndexNotSet = 2**64 - 1; uint8 public constant liquidityProviderTokenDecimals = 18; // 1 week at 3s/block uint256 public constant maxChainPropagationPeriod = (7 * 24 * 60 * 60) / 3; // 20% uint64 public constant maxFeeBasisPoints = 20 * 100; // Pool reserve balance ratio above which price dips below 1 pip and can no longer be represented uint64 public constant maxLiquidityPoolReserveRatio = 10**8; // Pool reserve balance below which prices can no longer be represented with full pip precision uint64 public constant minLiquidityPoolReserveInPips = 10**8; // 2% uint64 public constant maxPoolInputFeeBasisPoints = 2 * 100; // 5% uint64 public constant maxPoolOutputAdjustmentBasisPoints = 5 * 100; // 1% uint64 public constant maxPoolPriceCorrectionBasisPoints = 1 * 100; // To convert integer pips to a fractional price shift decimal left by the pip precision of 8 // decimals places uint64 public constant pipPriceMultiplier = 10**8; uint8 public constant signatureHashVersion = 3; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.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) { // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { // 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))) } } else if (signature.length == 64) { // 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 { let vs := mload(add(signature, 0x40)) r := mload(add(signature, 0x20)) s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } } else { revert("ECDSA: invalid signature length"); } return recover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { // 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. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); require(v == 27 || v == 28, "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 * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * 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)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; /** * @notice Enums definitions */ // Liquidity pools // enum LiquidityChangeOrigination { OnChain, OffChain } enum LiquidityChangeType { Addition, Removal } enum LiquidityChangeState { NotInitiated, Initiated, Executed } // Order book // 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 } // Withdrawals // enum WithdrawalType { BySymbol, ByAddress }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { ECDSA } from './ECDSA.sol'; import { Constants } from './Constants.sol'; import { LiquidityChangeType, WithdrawalType } from './Enums.sol'; import { LiquidityAddition, LiquidityRemoval, Order, Withdrawal } from './Structs.sol'; /** * @notice Library helpers for building hashes and verifying wallet signatures */ library Hashing { function isSignatureValid( bytes32 hash, bytes memory signature, address signer ) internal pure returns (bool) { return ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), signature) == signer; } // Hash construction // function getLiquidityAdditionHash(LiquidityAddition memory addition) internal pure returns (bytes32) { require( addition.signatureHashVersion == Constants.signatureHashVersion, 'Signature hash version invalid' ); return keccak256( abi.encodePacked( addition.signatureHashVersion, uint8(LiquidityChangeType.Addition), uint8(addition.origination), addition.nonce, addition.wallet, addition.assetA, addition.assetB, addition.amountADesired, addition.amountBDesired, addition.amountAMin, addition.amountBMin, addition.to, addition.deadline ) ); } function getLiquidityRemovalHash(LiquidityRemoval memory removal) internal pure returns (bytes32) { require( removal.signatureHashVersion == Constants.signatureHashVersion, 'Signature hash version invalid' ); return keccak256( abi.encodePacked( removal.signatureHashVersion, uint8(LiquidityChangeType.Removal), uint8(removal.origination), removal.nonce, removal.wallet, removal.assetA, removal.assetB, removal.liquidity, removal.amountAMin, removal.amountBMin, removal.to, removal.deadline ) ); } /** * @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 */ function getOrderHash( Order memory order, string memory baseSymbol, string memory quoteSymbol ) internal pure returns (bytes32) { require( order.signatureHashVersion == Constants.signatureHashVersion, 'Signature hash version invalid' ); // Placing all the fields in a single `abi.encodePacked` call causes a `stack too deep` error return keccak256( abi.encodePacked( abi.encodePacked( order.signatureHashVersion, order.nonce, order.walletAddress, string(abi.encodePacked(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 getWithdrawalHash(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 == WithdrawalType.BySymbol ? abi.encodePacked(withdrawal.assetSymbol) : abi.encodePacked(withdrawal.assetAddress), pipToDecimal(withdrawal.grossQuantityInPips), withdrawal.autoDispatchEnabled ) ); } /** * @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); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { Order, OrderBookTrade, Withdrawal } from './Structs.sol'; /** * @notice Interface of the ERC20 standard as defined in the EIP, but with no return values for * transfer and transferFrom. By asserting expected balance changes when calling these two methods * we can safely ignore their return values. This allows support of non-compliant tokens that do not * return a boolean. See https://github.com/ethereum/solidity/issues/4116 */ interface IERC20 { /** * @notice Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @notice Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @notice Moves `amount` tokens from the caller's account to `recipient`. * * Most implementing contracts return a boolean value indicating whether the operation succeeded, but * we ignore this and rely on asserting balance changes instead * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external; /** * @notice Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @notice Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @notice Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Most implementing contracts return a boolean value indicating whether the operation succeeded, but * we ignore this and rely on asserting balance changes instead * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external; /** * @notice Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @notice Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } /** * @notice Interface to Custodian contract. Used by Exchange and Governance contracts for internal * delegate calls */ interface ICustodian { /** * @notice ETH can only be sent by the Exchange */ receive() external payable; /** * @notice Withdraw any asset and amount to a target wallet * * @dev No balance checking performed * * @param wallet The wallet to which assets will be returned * @param asset The address of the asset to withdraw (native asset 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 Whistler Exchange contract * * @dev Used for lazy balance migrations from old to new Exchange after upgrade */ interface IExchange { /** * @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); /** * @notice Load the address of the Custodian contract * * @return The address of the Custodian contract */ function loadCustodian() external view returns (ICustodian); } interface ILiquidityProviderToken { function custodian() external returns (ICustodian); function baseAssetAddress() external returns (address); function quoteAssetAddress() external returns (address); function baseAssetSymbol() external returns (string memory); function quoteAssetSymbol() external returns (string memory); function token0() external returns (address); function token1() external returns (address); function burn( address wallet, uint256 liquidity, uint256 baseAssetQuantityInAssetUnits, uint256 quoteAssetQuantityInAssetUnits, address to ) external; function mint( address wallet, uint256 liquidity, uint256 baseAssetQuantityInAssetUnits, uint256 quoteAssetQuantityInAssetUnits, address to ) external; function reverseAssets() external; } interface IWETH9 is IERC20 { receive() external payable; function deposit() external payable; function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; library Math { function multiplyPipsByFraction( uint64 multiplicand, uint64 fractionDividend, uint64 fractionDivisor ) internal pure returns (uint64) { return multiplyPipsByFraction( multiplicand, fractionDividend, fractionDivisor, false ); } function multiplyPipsByFraction( uint64 multiplicand, uint64 fractionDividend, uint64 fractionDivisor, bool roundUp ) internal pure returns (uint64) { uint256 dividend = uint256(multiplicand) * fractionDividend; uint256 result = dividend / fractionDivisor; if (roundUp && dividend % fractionDivisor > 0) { result += 1; } require(result < 2**64, 'Pip quantity overflows uint64'); return uint64(result); } function min(uint64 x, uint64 y) internal pure returns (uint64 z) { z = x < y ? x : y; } function sqrt(uint256 y) internal pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { OrderSide } from './Enums.sol'; import { PoolTrade } from './Structs.sol'; library PoolTradeHelpers { /** * @dev Address of asset order wallet is receiving from pool */ function getOrderCreditAssetAddress( PoolTrade memory self, OrderSide orderSide ) internal pure returns (address) { return orderSide == OrderSide.Buy ? self.baseAssetAddress : self.quoteAssetAddress; } /** * @dev Address of asset order wallet is giving to pool */ function getOrderDebitAssetAddress(PoolTrade memory self, OrderSide orderSide) internal pure returns (address) { return orderSide == OrderSide.Buy ? self.quoteAssetAddress : self.baseAssetAddress; } /** * @dev Quantity in pips of asset that order wallet is receiving from pool */ function calculateOrderCreditQuantityInPips( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return ( orderSide == OrderSide.Buy ? self.netBaseQuantityInPips : self.netQuoteQuantityInPips ) - self.takerGasFeeQuantityInPips; } /** * @dev Quantity in pips of asset that order wallet is giving to pool */ function getOrderDebitQuantityInPips( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return orderSide == OrderSide.Buy ? self.grossQuoteQuantityInPips : self.grossBaseQuantityInPips; } /** * @dev Quantity in pips of asset that pool receives from order wallet */ function calculatePoolCreditQuantityInPips( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return ( orderSide == OrderSide.Buy ? self.netQuoteQuantityInPips : self.netBaseQuantityInPips ) + self.takerPoolFeeQuantityInPips; } /** * @dev Quantity in pips of asset that leaves pool as output */ function getPoolDebitQuantityInPips( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return ( orderSide == OrderSide.Buy ? self.netBaseQuantityInPips // Pool gives net base asset plus taker gas fee : self.netQuoteQuantityInPips // Pool gives net quote asset plus taker gas fee ); } /** * @dev Gross quantity received by order wallet */ function getOrderGrossReceivedQuantityInPips( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return orderSide == OrderSide.Buy ? self.grossBaseQuantityInPips : self.grossQuoteQuantityInPips; } function calculatePoolOutputAdjustment( PoolTrade memory self, OrderSide orderSide ) internal pure returns (uint64) { return getOrderGrossReceivedQuantityInPips(self, orderSide) - getPoolDebitQuantityInPips(self, orderSide); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { ILiquidityProviderToken, IWETH9 } from './Interfaces.sol'; import { LiquidityChangeOrigination, OrderSelfTradePrevention, OrderSide, OrderTimeInForce, OrderType, WithdrawalType } from './Enums.sol'; /** * @notice Struct definitions */ /** * @notice State tracking for a hybrid liquidity pool * * @dev Base and quote asset decimals are denormalized here to avoid extra loads from * `AssetRegistry.Storage` */ struct LiquidityPool { // Flag to distinguish from empty struct bool exists; uint64 baseAssetReserveInPips; uint8 baseAssetDecimals; uint64 quoteAssetReserveInPips; uint8 quoteAssetDecimals; ILiquidityProviderToken liquidityProviderToken; } /** * @dev Internal struct capturing user-initiated liquidity addition request parameters */ struct LiquidityAddition { // Must equal `Constants.signatureHashVersion` uint8 signatureHashVersion; // Distinguishes between liquidity additions initated on- or off- chain LiquidityChangeOrigination origination; // UUIDv1 unique to wallet uint128 nonce; address wallet; address assetA; address assetB; uint256 amountADesired; uint256 amountBDesired; uint256 amountAMin; uint256 amountBMin; address to; uint256 deadline; bytes signature; } /** * @notice Internally used struct, return type from `LiquidityPools.addLiquidity` */ struct LiquidityAdditionDepositResult { string assetASymbol; uint64 assetAQuantityInPips; uint64 assetANewExchangeBalanceInPips; uint256 assetANewExchangeBalanceInAssetUnits; string assetBSymbol; uint64 assetBQuantityInPips; uint64 assetBNewExchangeBalanceInPips; uint256 assetBNewExchangeBalanceInAssetUnits; } /** * @notice Internally used struct, return type from `LiquidityPools.removeLiquidity` */ struct LiquidityRemovalDepositResult { address assetAddress; string assetSymbol; uint64 assetQuantityInPips; uint64 assetNewExchangeBalanceInPips; uint256 assetNewExchangeBalanceInAssetUnits; } /** * @dev Internal struct capturing user-initiated liquidity removal request parameters */ struct LiquidityRemoval { // Must equal `Constants.signatureHashVersion` uint8 signatureHashVersion; // Distinguishes between liquidity additions initated on- or off- chain LiquidityChangeOrigination origination; uint128 nonce; address wallet; address assetA; address assetB; uint256 liquidity; uint256 amountAMin; uint256 amountBMin; address payable to; uint256 deadline; bytes signature; } /** * @notice Argument type to `Exchange.executeAddLiquidity` and `Exchange.executeRemoveLiquidity` */ struct LiquidityChangeExecution { address baseAssetAddress; address quoteAssetAddress; uint64 liquidityInPips; // 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 sent to pool for additions or received by wallet for removals uint64 netBaseQuantityInPips; // Net amount of quote asset sent to pool for additions or received by wallet for removals uint64 netQuoteQuantityInPips; } /** * @notice Internally used struct, argument type to `LiquidityPoolAdmin.migrateLiquidityPool` */ struct LiquidityMigration { address token0; address token1; bool isToken1Quote; uint256 desiredLiquidity; address to; IWETH9 WETH; } /** * @notice Internally used struct capturing wallet order nonce invalidations created via `invalidateOrderNonce` */ struct NonceInvalidation { bool exists; uint64 timestampInMs; uint256 effectiveBlockNumber; } /** * @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.executeOrderBookTrade` and `Hashing.getOrderWalletHash` */ struct Order { // Must equal `Constants.signatureHashVersion` uint8 signatureHashVersion; // UUIDv1 unique to wallet uint128 nonce; // Wallet address that placed order and signed hash address walletAddress; // Type of order OrderType orderType; // Order side wallet is on 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 OrderTimeInForce timeInForce; // STP behavior specified by wallet for order OrderSelfTradePrevention selfTradePrevention; // Cancellation time specified by wallet for GTT TIF order uint64 cancelAfter; // The ECDSA signature of the order hash as produced by Hashing.getOrderWalletHash bytes walletSignature; } /** * @notice Argument type for `Exchange.executeOrderBookTrade` specifying execution parameters for matching orders */ struct OrderBookTrade { // 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, inclusive of gas fees 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 OrderSide makerSide; } /** * @notice Argument type for `Exchange.executePoolTrade` specifying execution parameters for an * order against pool liquidity */ struct PoolTrade { // 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; // If wallet is buy side, net amount of quote input to pool used to calculate output; otherwise, // net amount of base asset leaving pool uint64 netBaseQuantityInPips; // If wallet is buy side, net amount of base input to pool used to calculate output; otherwise, // net amount of quote asset leaving pool uint64 netQuoteQuantityInPips; // Fee paid by liquidity taker to pool from sent asset uint64 takerPoolFeeQuantityInPips; // Fee paid by liquidity taker to fee wallet from sent asset uint64 takerProtocolFeeQuantityInPips; // Fee paid by liquidity taker to fee wallet from received asset uint64 takerGasFeeQuantityInPips; // Fee paid by liquidity taker sell to pool taken from pool's quote asset output uint64 takerPriceCorrectionFeeQuantityInPips; } struct HybridTrade { OrderBookTrade orderBookTrade; PoolTrade poolTrade; // Fee paid by liquidity taker to fee wallet from received asset uint64 takerGasFeeQuantityInPips; } /** * @notice Argument type for `Exchange.withdraw` and `Hashing.getWithdrawalWalletHash` */ struct Withdrawal { // Distinguishes between withdrawals by asset symbol or address 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 grossQuantityInPips; // 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 Hashing.getWithdrawalWalletHash bytes walletSignature; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; /** * Library helper for extracting timestamp component of Version 1 UUIDs */ library UUID { /** * 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) - 12219292800000; return msSinceUnixEpoch; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { AssetRegistry } from './AssetRegistry.sol'; import { AssetUnitConversions } from './AssetUnitConversions.sol'; import { Constants } from './Constants.sol'; import { Hashing } from './Hashing.sol'; import { IERC20 } from './Interfaces.sol'; import { Math } from './Math.sol'; import { UUID } from './UUID.sol'; import { OrderSide, OrderType } from './Enums.sol'; import { Asset, LiquidityPool, Order, OrderBookTrade, NonceInvalidation, PoolTrade, Withdrawal } from './Structs.sol'; library Validations { using AssetRegistry for AssetRegistry.Storage; /** * @dev Perform fee validations common to both orderbook-only and hybrid trades. Does not * validate if fees are excessive as taker fee structure differs between these trade types * */ function validateOrderBookTradeFees(OrderBookTrade memory trade) internal pure { require( trade.netBaseQuantityInPips + ( trade.makerFeeAssetAddress == trade.baseAssetAddress ? trade.makerFeeQuantityInPips : trade.takerFeeQuantityInPips ) == trade.grossBaseQuantityInPips, 'Orderbook base fees unbalanced' ); require( trade.netQuoteQuantityInPips + ( trade.makerFeeAssetAddress == trade.quoteAssetAddress ? trade.makerFeeQuantityInPips : trade.takerFeeQuantityInPips ) == trade.grossQuoteQuantityInPips, 'Orderbook quote fees unbalanced' ); } function validateOrderNonce( Order memory order, mapping(address => NonceInvalidation) storage nonceInvalidations ) internal view { require( UUID.getTimestampInMsFromUuidV1(order.nonce) > loadLastInvalidatedTimestamp(order.walletAddress, nonceInvalidations), 'Order nonce timestamp too low' ); } function validateOrderNonces( Order memory buy, Order memory sell, mapping(address => NonceInvalidation) storage nonceInvalidations ) internal view { require( UUID.getTimestampInMsFromUuidV1(buy.nonce) > loadLastInvalidatedTimestamp(buy.walletAddress, nonceInvalidations), 'Buy order nonce timestamp too low' ); require( UUID.getTimestampInMsFromUuidV1(sell.nonce) > loadLastInvalidatedTimestamp(sell.walletAddress, nonceInvalidations), 'Sell order nonce timestamp too low' ); } function validateOrderSignature( Order memory order, string memory baseAssetSymbol, string memory quoteAssetSymbol ) internal pure returns (bytes32) { bytes32 orderHash = Hashing.getOrderHash(order, baseAssetSymbol, quoteAssetSymbol); require( Hashing.isSignatureValid( orderHash, order.walletSignature, order.walletAddress ), order.side == OrderSide.Buy ? 'Invalid wallet signature for buy order' : 'Invalid wallet signature for sell order' ); return orderHash; } function validatePoolReserveRatio(LiquidityPool memory pool) internal pure { (uint64 sortedReserve0, uint64 sortedReserve1) = pool.baseAssetReserveInPips <= pool.quoteAssetReserveInPips ? (pool.baseAssetReserveInPips, pool.quoteAssetReserveInPips) : (pool.quoteAssetReserveInPips, pool.baseAssetReserveInPips); require( uint256(sortedReserve0) * Constants.maxLiquidityPoolReserveRatio >= sortedReserve1, 'Exceeded max reserve ratio' ); } /** * @dev Perform fee validations common to both pool-only and hybrid trades */ function validatePoolTradeInputFees( OrderSide orderSide, PoolTrade memory poolTrade ) internal pure { // Buy order sends quote as pool input, receives base as pool output; sell order sends base as // pool input, receives quote as pool output (uint64 netInputQuantityInPips, uint64 grossInputQuantityInPips) = orderSide == OrderSide.Buy ? (poolTrade.netQuoteQuantityInPips, poolTrade.grossQuoteQuantityInPips) : (poolTrade.netBaseQuantityInPips, poolTrade.grossBaseQuantityInPips); require( netInputQuantityInPips + poolTrade.takerPoolFeeQuantityInPips + poolTrade.takerProtocolFeeQuantityInPips == grossInputQuantityInPips, 'Pool input fees unbalanced' ); require( Validations.isFeeQuantityValid( grossInputQuantityInPips - netInputQuantityInPips, grossInputQuantityInPips, Constants.maxPoolInputFeeBasisPoints ), 'Excessive pool input fee' ); } function validateWithdrawalSignature(Withdrawal memory withdrawal) internal pure returns (bytes32) { bytes32 withdrawalHash = Hashing.getWithdrawalHash(withdrawal); require( Hashing.isSignatureValid( withdrawalHash, withdrawal.walletSignature, withdrawal.walletAddress ), 'Invalid wallet signature' ); return withdrawalHash; } // Utils // function calculateImpliedQuoteQuantityInPips( uint64 baseQuantityInPips, uint64 limitPriceInPips ) internal pure returns (uint64) { return Math.multiplyPipsByFraction( baseQuantityInPips, limitPriceInPips, Constants.pipPriceMultiplier ); } function loadLastInvalidatedTimestamp( address walletAddress, mapping(address => NonceInvalidation) storage nonceInvalidations ) private view returns (uint64) { if ( nonceInvalidations[walletAddress].exists && nonceInvalidations[walletAddress].effectiveBlockNumber <= block.number ) { return nonceInvalidations[walletAddress].timestampInMs; } return 0; } function isFeeQuantityValid( uint64 fee, uint64 total, uint64 max ) internal pure returns (bool) { uint64 feeBasisPoints = (fee * Constants.basisPointsInTotal) / total; return feeBasisPoints <= max; } function isLimitOrderType(OrderType orderType) internal pure returns (bool) { return orderType == OrderType.Limit || orderType == OrderType.LimitMaker || orderType == OrderType.StopLossLimit || orderType == OrderType.TakeProfitLimit; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.4; import { AssetRegistry } from './AssetRegistry.sol'; import { AssetUnitConversions } from './AssetUnitConversions.sol'; import { BalanceTracking } from './BalanceTracking.sol'; import { Constants } from './Constants.sol'; import { UUID } from './UUID.sol'; import { Validations } from './Validations.sol'; import { WithdrawalType } from './Enums.sol'; import { Asset, LiquidityChangeExecution, LiquidityRemoval, Withdrawal } from './Structs.sol'; import { ICustodian, ILiquidityProviderToken } from './Interfaces.sol'; library Withdrawing { using AssetRegistry for AssetRegistry.Storage; using BalanceTracking for BalanceTracking.Storage; function withdraw( Withdrawal memory withdrawal, ICustodian custodian, address feeWallet, AssetRegistry.Storage storage assetRegistry, BalanceTracking.Storage storage balanceTracking, mapping(bytes32 => bool) storage completedWithdrawalHashes ) public returns ( uint64 newExchangeBalanceInPips, uint256 newExchangeBalanceInAssetUnits, address assetAddress, string memory assetSymbol ) { // Validations require( Validations.isFeeQuantityValid( withdrawal.gasFeeInPips, withdrawal.grossQuantityInPips, Constants.maxFeeBasisPoints ), 'Excessive withdrawal fee' ); bytes32 withdrawalHash = Validations.validateWithdrawalSignature(withdrawal); require( !completedWithdrawalHashes[withdrawalHash], 'Hash already withdrawn' ); // If withdrawal is by asset symbol (most common) then resolve to asset address Asset memory asset = withdrawal.withdrawalType == WithdrawalType.BySymbol ? assetRegistry.loadAssetBySymbol( withdrawal.assetSymbol, UUID.getTimestampInMsFromUuidV1(withdrawal.nonce) ) : assetRegistry.loadAssetByAddress(withdrawal.assetAddress); assetSymbol = asset.symbol; assetAddress = asset.assetAddress; // Update wallet balances newExchangeBalanceInPips = balanceTracking.updateForWithdrawal( withdrawal, asset.assetAddress, feeWallet ); newExchangeBalanceInAssetUnits = AssetUnitConversions.pipsToAssetUnits( newExchangeBalanceInPips, asset.decimals ); // Transfer funds from Custodian to wallet uint256 netAssetQuantityInAssetUnits = AssetUnitConversions.pipsToAssetUnits( withdrawal.grossQuantityInPips - withdrawal.gasFeeInPips, asset.decimals ); custodian.withdraw( withdrawal.walletAddress, asset.assetAddress, netAssetQuantityInAssetUnits ); // Replay prevention completedWithdrawalHashes[withdrawalHash] = true; } function withdrawExit( address assetAddress, ICustodian custodian, AssetRegistry.Storage storage assetRegistry, BalanceTracking.Storage storage balanceTracking ) external returns (uint64 previousExchangeBalanceInPips) { // Update wallet balance previousExchangeBalanceInPips = balanceTracking.updateForExit( msg.sender, assetAddress ); // Transfer asset from Custodian to wallet Asset memory asset = assetRegistry.loadAssetByAddress(assetAddress); uint256 balanceInAssetUnits = AssetUnitConversions.pipsToAssetUnits( previousExchangeBalanceInPips, asset.decimals ); ICustodian(custodian).withdraw( payable(msg.sender), assetAddress, balanceInAssetUnits ); } function withdrawLiquidity( LiquidityRemoval memory removal, LiquidityChangeExecution memory execution, ICustodian custodian, address feeWallet, ILiquidityProviderToken liquidityProviderToken, AssetRegistry.Storage storage assetRegistry, BalanceTracking.Storage storage balanceTracking ) internal { ( uint64 outputBaseAssetQuantityInPips, uint64 outputQuoteAssetQuantityInPips ) = balanceTracking.updateForRemoveLiquidity( removal, execution, feeWallet, address(custodian), liquidityProviderToken ); Asset memory asset; if (outputBaseAssetQuantityInPips > 0) { asset = assetRegistry.loadAssetByAddress(execution.baseAssetAddress); custodian.withdraw( removal.to, execution.baseAssetAddress, AssetUnitConversions.pipsToAssetUnits( outputBaseAssetQuantityInPips, asset.decimals ) ); } if (outputQuoteAssetQuantityInPips > 0) { asset = assetRegistry.loadAssetByAddress(execution.quoteAssetAddress); custodian.withdraw( removal.to, execution.quoteAssetAddress, AssetUnitConversions.pipsToAssetUnits( outputQuoteAssetQuantityInPips, asset.decimals ) ); } } }
{ "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "optimizer": { "enabled": true, "runs": 1 }, "evmVersion": "berlin", "metadata": { "bytecodeHash": "ipfs" }, "libraries": { "Exchange.sol": { "AssetRegistry": "0xc2f05d03915E7c2D9038830F7888c97e351dd3dB", "Depositing": "0x116310b243dd287d4285d0e8a34ce3d4adb63dac", "LiquidityPoolAdmin": "0x7a246e4434dd31df784bb88d3443e309e3143adc", "LiquidityPools": "0x0f2c07f4ecc6c9d74d16e735d2a59d00985b1962", "NonceInvalidations": "0x6c539e6143f70408076f35d19e7e549850c021ad", "Trading": "0x4d3250014ea4ecddd857fad48c3d64d2e4f037e1", "Withdrawing": "0xb3af24eeac0ee8b6f5798f8a75e3ecd51b18deb2" }, "AssetRegistry.sol": { "AssetRegistry": "0xc2f05d03915E7c2D9038830F7888c97e351dd3dB" }, "Depositing.sol": { "Depositing": "0x116310b243dd287d4285d0e8a34ce3d4adb63dac" }, "LiquidityPoolAdmin.sol": { "LiquidityPoolAdmin": "0x7a246e4434dd31df784bb88d3443e309e3143adc" }, "LiquidityPools.sol": { "LiquidityPools": "0x0f2c07f4ecc6c9d74d16e735d2a59d00985b1962" }, "NonceInvalidations.sol": { "NonceInvalidations": "0x6c539e6143f70408076f35d19e7e549850c021ad" }, "Trading.sol": { "Trading": "0x4d3250014ea4ecddd857fad48c3d64d2e4f037e1" }, "Withdrawing.sol": { "Withdrawing": "0xb3af24eeac0ee8b6f5798f8a75e3ecd51b18deb2" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract Creation Code
611a7761003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100405760003560e01c8063b0f2e4a614610045578063c42cc91f1461007e575b600080fd5b81801561005157600080fd5b5061006561006036600461143c565b6100b6565b604051610075949392919061164b565b60405180910390f35b81801561008a57600080fd5b5061009e6100993660046113f7565b6102c9565b6040516001600160401b039091168152602001610075565b600080600060606100d28a60c001518b60a001516107d0610365565b61011e5760405162461bcd60e51b8152602060048201526018602482015277457863657373697665207769746864726177616c2066656560401b60448201526064015b60405180910390fd5b60006101298b610399565b60008181526020889052604090205490915060ff16156101845760405162461bcd60e51b81526020600482015260166024820152752430b9b41030b63932b0b23c903bb4ba34323930bbb760511b6044820152606401610115565b6000808c5160018111156101a857634e487b7160e01b600052602160045260246000fd5b146101c25760808c01516101bd908a90610408565b6101df565b6101df8c606001516101d78e60200151610629565b8b91906106c5565b604081015160208201519550935090506101fb888d868d610a25565b955061020b868260600151610af9565b945060006102308d60c001518e60a0015161022691906118b6565b8360600151610af9565b6040808f015160208501519151636ce5768960e11b81529293506001600160a01b038f169263d9caed129261026a92918690600401611627565b600060405180830381600087803b15801561028457600080fd5b505af1158015610298573d6000803e3d6000fd5b50505060009384525050506020959095526040909420805460ff191660011790559198909750909550909350915050565b60006102d6823387610bbd565b905060006102e48487610408565b905060006102f6838360600151610af9565b604051636ce5768960e11b81529091506001600160a01b0387169063d9caed12906103299033908b908690600401611627565b600060405180830381600087803b15801561034357600080fd5b505af1158015610357573d6000803e3d6000fd5b505050505050949350505050565b6000808361037561271087611870565b61037f9190611749565b6001600160401b03808516911611159150505b9392505050565b6000806103a583610c39565b90506103bb818461010001518560400151610d1b565b6104025760405162461bcd60e51b8152602060048201526018602482015277496e76616c69642077616c6c6574207369676e617475726560401b6044820152606401610115565b92915050565b6104106112ea565b6001600160a01b0382166104ba576104b383600201805461043090611948565b80601f016020809104026020016040519081016040528092919081815260200182805461045c90611948565b80156104a95780601f1061047e576101008083540402835291602001916104a9565b820191906000526020600020905b81548152906001019060200180831161048c57829003601f168201915b5050505050610d4b565b9050610402565b6001600160a01b03808316600090815260208581526040808320815160c081018352815460ff8116151582526101009004909516928501929092526001820180549394939184019161050b90611948565b80601f016020809104026020016040519081016040528092919081815260200182805461053790611948565b80156105845780601f1061055957610100808354040283529160200191610584565b820191906000526020600020905b81548152906001019060200180831161056757829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b0316606090910152805190915080156105d1575080608001515b6103925760405162461bcd60e51b8152602060048201526024808201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f72206164646044820152637265737360e01b6064820152608401610115565b6000600f604c83901c16600181146106755760405162461bcd60e51b815260206004820152600f60248201526e135d5cdd081899481d8c4815555251608a1b6044820152606401610115565b610fff60301b601084901c1661ffff60201b603085901c1663ffffffff606086901c168183178117650b1d069b54006106b06127108361170f565b6106ba91906118b6565b979650505050505050565b6106cd6112ea565b6107638460020180546106df90611948565b80601f016020809104026020016040519081016040528092919081815260200182805461070b90611948565b80156107585780601f1061072d57610100808354040283529160200191610758565b820191906000526020600020905b81548152906001019060200180831161073b57829003601f168201915b505050505084610d8a565b156107815761077a84600201805461043090611948565b9050610392565b6107896112ea565b6000856001018560405161079d919061159c565b9081526040519081900360200190205411156109b65760005b85600101856040516107c8919061159c565b9081526040519081900360200190205460ff821610156109b457836001600160401b031686600101866040516107fe919061159c565b90815260200160405180910390208260ff168154811061082e57634e487b7160e01b600052603260045260246000fd5b60009182526020909120600390910201600201546201000090046001600160401b0316116109a2578560010185604051610868919061159c565b90815260200160405180910390208160ff168154811061089857634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160c0810182526003909302909101805460ff8116151584526001600160a01b036101009091041693830193909352600183018054929392918401916108eb90611948565b80601f016020809104026020016040519081016040528092919081815260200182805461091790611948565b80156109645780601f1061093957610100808354040283529160200191610964565b820191906000526020600020905b81548152906001019060200180831161094757829003601f168201915b50505091835250506002919091015460ff8082166020840152610100820416151560408301526201000090046001600160401b031660609091015291505b806109ac8161199e565b9150506107b6565b505b805180156109c5575080608001515b610a1d5760405162461bcd60e51b815260206004820152602360248201527f4e6f20636f6e6669726d656420617373657420666f756e6420666f722073796d604482015262189bdb60ea1b6064820152608401610115565b949350505050565b600080610a3786866040015186610de3565b60a08601518154919250908290600190610a6090849061010090046001600160401b03166118b6565b82546001600160401b0391821661010093840a9081029083021990911617909255835460c08901519190048216945016159050610af057610aa2868486610de3565b60c08601518154919250908290600190610acb90849061010090046001600160401b03166116e4565b92506101000a8154816001600160401b0302191690836001600160401b031602179055505b50949350505050565b600060208260ff161115610b5f5760405162461bcd60e51b815260206004820152602760248201527f41737365742063616e6e6f742068617665206d6f7265207468616e20333220646044820152666563696d616c7360c81b6064820152608401610115565b60088260ff161115610b9457610b766008836118de565b610b8190600a6117a6565b6104b3906001600160401b038516611851565b610b9f8260086118de565b610baa90600a6117a6565b610392906001600160401b038516611735565b600080610bcb858585610de3565b805461010090046001600160401b03169250905081610c235760405162461bcd60e51b8152602060048201526014602482015273139bc818985b185b98d948199bdc88185cdcd95d60621b6044820152606401610115565b8054610100600160481b03191690559392505050565b60208101516040820151600091908284516001811115610c6957634e487b7160e01b600052602160045260246000fd5b14610caa578360800151604051602001610c96919060609190911b6001600160601b031916815260140190565b604051602081830303815290604052610ccf565b8360600151604051602001610cbf919061159c565b6040516020818303038152906040525b610ce58560a001516001600160401b0316610ee6565b8560e00151604051602001610cfe9594939291906115b8565b604051602081830303815290604052805190602001209050919050565b6000816001600160a01b0316610d39610d3386611060565b8561109a565b6001600160a01b031614949350505050565b610d536112ea565b506040805160c08101825260018082526000602083018190529282019390935260126060820152608081019290925260a082015290565b600081604051602001610d9d919061159c565b6040516020818303038152906040528051906020012083604051602001610dc4919061159c565b6040516020818303038152906040528051906020012014905092915050565b6001600160a01b038083166000908152602085815260408083209385168352929052908120805460ff16158015610e26575060018501546001600160a01b031615155b15610a1d57600185015460405163dbb3653560e01b81526001600160a01b03868116600483015285811660248301529091169063dbb365359060440160206040518083038186803b158015610e7a57600080fd5b505afa158015610e8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb29190611580565b815460ff196001600160401b039290921661010002919091166001600160481b031990911617600117815590509392505050565b60608160005b8115610f125780610efc81611983565b9150610f0b9050600a83611735565b9150610eec565b6009811015610f1f575060095b80610f2981611983565b9150506000816001600160401b03811115610f5457634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f191660200182016040528015610f7e576020820181803683370190505b509050815b8015610af057610f93818461189f565b60081415610fe657601760f91b82610fac60018461189f565b81518110610fca57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535061104e565b610ff1600a876119be565b610ffc9060306116cc565b60f81b8261100b60018461189f565b8151811061102957634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535061104b600a87611735565b95505b8061105881611931565b915050610f83565b6040517b0ca2ba3432b932bab69029b4b3b732b21026b2b9b9b0b3b29d05199960211b6020820152603c8101829052600090605c01610cfe565b6000806000808451604114156110c45750505060208201516040830151606084015160001a61113a565b8451604014156110f25750505060408201516020830151906001600160ff1b0381169060ff1c601b0161113a565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610115565b61114686828585611150565b9695505050505050565b60006fa2a8918ca85bafe22016d0b997e4df60600160ff1b038211156111c35760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610115565b8360ff16601b14806111d857508360ff16601c145b61122f5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610115565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015611283573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166112e15760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b6044820152606401610115565b95945050505050565b6040805160c08101825260008082526020820181905260609282018390529181018290526080810182905260a081019190915290565b803561132b81611a14565b919050565b8035801515811461132b57600080fd5b600082601f830112611350578081fd5b81356001600160401b038082111561136a5761136a6119fe565b604051601f8301601f19908116603f01168101908282118183101715611392576113926119fe565b816040528381528660208588010111156113aa578485fd5b8360208701602083013792830160200193909352509392505050565b80356002811061132b57600080fd5b80356001600160801b038116811461132b57600080fd5b803561132b81611a2c565b6000806000806080858703121561140c578384fd5b843561141781611a14565b9350602085013561142781611a14565b93969395505050506040820135916060013590565b60008060008060008060c08789031215611454578182fd5b86356001600160401b038082111561146a578384fd5b90880190610120828b03121561147e578384fd5b6114866116a3565b61148f836113c6565b815261149d602084016113d5565b60208201526114ae60408401611320565b60408201526060830135828111156114c4578586fd5b6114d08c828601611340565b6060830152506114e260808401611320565b60808201526114f360a084016113ec565b60a082015261150460c084016113ec565b60c082015261151560e08401611330565b60e0820152610100808401358381111561152d578687fd5b6115398d828701611340565b82840152505080985050505061155160208801611320565b945061155f60408801611320565b9350606087013592506080870135915060a087013590509295509295509295565b600060208284031215611591578081fd5b815161039281611a2c565b600082516115ae818460208701611901565b9190910192915050565b608086901b6001600160801b0319168152606085901b6001600160601b031916601082015283516000906115f3816024850160208901611901565b84519083019061160a816024840160208901611901565b93151560f81b930160248101939093525050602501949350505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60018060401b038516815283602082015260018060a01b0383166040820152608060608201526000825180608084015261168c8160a0850160208701611901565b601f01601f19169190910160a00195945050505050565b60405161012081016001600160401b03811182821017156116c6576116c66119fe565b60405290565b600082198211156116df576116df6119d2565b500190565b60006001600160401b03828116848216808303821115611706576117066119d2565b01949350505050565b60006001600160801b0383811680611729576117296119e8565b92169190910492915050565b600082611744576117446119e8565b500490565b60006001600160401b0383811680611729576117296119e8565b600181815b8085111561179e578160001904821115611784576117846119d2565b8085161561179157918102915b93841c9390800290611768565b509250929050565b600061039260ff8416836000826117bf57506001610402565b816117cc57506000610402565b81600181146117e257600281146117ec57611808565b6001915050610402565b60ff8411156117fd576117fd6119d2565b50506001821b610402565b5060208310610133831016604e8410600b841016171561182b575081810a610402565b6118358383611763565b8060001904821115611849576118496119d2565b029392505050565b600081600019048311821515161561186b5761186b6119d2565b500290565b60006001600160401b0382811684821681151582840482111615611896576118966119d2565b02949350505050565b6000828210156118b1576118b16119d2565b500390565b60006001600160401b03838116908316818110156118d6576118d66119d2565b039392505050565b600060ff821660ff8416808210156118f8576118f86119d2565b90039392505050565b60005b8381101561191c578181015183820152602001611904565b8381111561192b576000848401525b50505050565b600081611940576119406119d2565b506000190190565b600181811c9082168061195c57607f821691505b6020821081141561197d57634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415611997576119976119d2565b5060010190565b600060ff821660ff8114156119b5576119b56119d2565b60010192915050565b6000826119cd576119cd6119e8565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611a2957600080fd5b50565b6001600160401b0381168114611a2957600080fdfea26469706673582212207fac73239b67f9206f701f29abd0bc658c1c450db9dafc60f6127dc32b06b03c64736f6c63430008040033
Deployed ByteCode Sourcemap
595:4289:15:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;722:2068;;;;;;;;;;-1:-1:-1;722:2068:15;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;2794:768;;;;;;;;;;-1:-1:-1;2794:768:15;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;11647:31:16;;;11629:50;;11617:2;11602:18;2794:768:15;11584:101:16;722:2068:15;1024:31;1063:38;1109:20;1137:25;1211:148;1251:10;:23;;;1284:10;:30;;;553:8:5;1211:30:15;:148::i;:::-;1196:203;;;;-1:-1:-1;;;1196:203:15;;9813:2:16;1196:203:15;;;9795:21:16;9852:2;9832:18;;;9825:30;-1:-1:-1;;;9871:18:16;;;9864:54;9935:18;;1196:203:15;;;;;;;;;1405:22;1436:51;1476:10;1436:39;:51::i;:::-;1509:41;;;;;;;;;;;;1405:82;;-1:-1:-1;1509:41:15;;1508:42;1493:95;;;;-1:-1:-1;;;1493:95:15;;10166:2:16;1493:95:15;;;10148:21:16;10205:2;10185:18;;;10178:30;-1:-1:-1;;;10224:18:16;;;10217:52;10286:18;;1493:95:15;10138:172:16;1493:95:15;1679:18;;1706:25;;:52;;;;;;-1:-1:-1;;;1706:52:15;;;;;;;;;;:267;;1949:23;;;;1916:57;;:13;;:32;:57::i;:::-;1706:267;;;1769:136;1812:10;:22;;;1846:49;1878:10;:16;;;1846:31;:49::i;:::-;1769:13;;:136;:31;:136::i;:::-;1994:12;;;;2027:18;;;;;-1:-1:-1;1994:12:15;-1:-1:-1;1679:294:15;-1:-1:-1;2109:102:15;:15;2152:10;2027:18;2196:9;2109:35;:102::i;:::-;2082:129;;2250:97;2295:24;2327:5;:14;;;2250:37;:97::i;:::-;2217:130;;2401:36;2446:135;2526:10;:23;;;2493:10;:30;;;:56;;;;:::i;:::-;2559:5;:14;;;2446:37;:135::i;:::-;2613:24;;;;;2645:18;;;;2587:118;;-1:-1:-1;;;2587:118:15;;2401:180;;-1:-1:-1;;;;;;2587:18:15;;;;;:118;;2613:24;2401:180;;2587:118;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;2737:41:15;;;;-1:-1:-1;;;2737:41:15;;;;;;;;;:48;;-1:-1:-1;;2737:48:15;2781:4;2737:48;;;722:2068;;;;-1:-1:-1;722:2068:15;;-1:-1:-1;722:2068:15;;-1:-1:-1;722:2068:15;-1:-1:-1;;722:2068:15:o;2794:768::-;2992:36;3097:73;:15;3134:10;3152:12;3097:29;:73::i;:::-;3065:105;-1:-1:-1;3224:18:15;3245:46;:13;3278:12;3245:32;:46::i;:::-;3224:67;;3297:27;3333:108;3380:29;3419:5;:14;;;3333:37;:108::i;:::-;3447:110;;-1:-1:-1;;;3447:110:15;;3297:144;;-1:-1:-1;;;;;;3447:30:15;;;;;:110;;3493:10;;3512:12;;3297:144;;3447:110;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2794:768;;;;;;;;:::o;5719:224:14:-;5824:4;;5899:5;5861:34;264:9:5;5861:3:14;:34;:::i;:::-;5860:44;;;;:::i;:::-;-1:-1:-1;;;;;5917:21:14;;;;;;;;-1:-1:-1;;5719:224:14;;;;;;:::o;4598:402::-;4700:7;4717:22;4742:37;4768:10;4742:25;:37::i;:::-;4717:62;;4801:126;4835:14;4859:10;:26;;;4895:10;:24;;;4801;:126::i;:::-;4786:181;;;;-1:-1:-1;;;4786:181:14;;7546:2:16;4786:181:14;;;7528:21:16;7585:2;7565:18;;;7558:30;-1:-1:-1;;;7604:18:16;;;7597:54;7668:18;;4786:181:14;7518:174:16;4786:181:14;4981:14;4598:402;-1:-1:-1;;4598:402:14:o;6291:411:1:-;6398:12;;:::i;:::-;-1:-1:-1;;;;;6424:28:1;;6420:91;;6469:35;6481:4;:22;;6469:35;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:11;:35::i;:::-;6462:42;;;;6420:91;-1:-1:-1;;;;;6538:34:1;;;6517:18;6538:34;;;;;;;;;;;6517:55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:18;;:55;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;6517:55:1;;;-1:-1:-1;;6517:55:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;6517:55:1;;;;;;6593:12;;;;-1:-1:-1;6593:33:1;;;;;6609:5;:17;;;6593:33;6578:100;;;;-1:-1:-1;;;6578:100:1;;10517:2:16;6578:100:1;;;10499:21:16;10556:2;10536:18;;;10529:30;10595:34;10575:18;;;10568:62;-1:-1:-1;;;10646:18:16;;;10639:34;10690:19;;6578:100:1;10489:226:16;307:886:13;392:23;515:34;509:2;501:10;;;500:49;574:1;563:12;;555:40;;;;-1:-1:-1;;;555:40:13;;8259:2:16;555:40:13;;;8241:21:16;8298:2;8278:18;;;8271:30;-1:-1:-1;;;8317:18:16;;;8310:45;8372:18;;555:40:13;8231:165:16;555:40:13;-1:-1:-1;;;707:2:13;699:10;;;698:49;-1:-1:-1;;;780:2:13;772:10;;;771:49;845:10;853:2;845:10;;;;932:18;;;:28;;1144:14;1111:29;1135:5;932:28;1111:29;:::i;:::-;1104:54;;;;:::i;:::-;1085:73;307:886;-1:-1:-1;;;;;;;307:886:13:o;7066:714:1:-;7198:12;;:::i;:::-;7222:45;7236:4;:22;;7222:45;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7260:6;7222:13;:45::i;:::-;7218:108;;;7284:35;7296:4;:22;;7284:35;;;;;:::i;:::-;7277:42;;;;7218:108;7332:18;;:::i;:::-;7397:1;7360:4;:19;;7380:6;7360:27;;;;;;:::i;:::-;;;;;;;;;;;;;;:34;:38;7356:296;;;7413:7;7408:238;7430:4;:19;;7450:6;7430:27;;;;;;:::i;:::-;;;;;;;;;;;;;;:34;7426:38;;;;7408:238;;;7553:13;-1:-1:-1;;;;;7496:70:1;:4;:19;;7516:6;7496:27;;;;;;:::i;:::-;;;;;;;;;;;;;7524:1;7496:30;;;;;;;;-1:-1:-1;;;7496:30:1;;;;;;;;;;;;;;;;;;;;;;:53;;;;;;-1:-1:-1;;;;;7496:53:1;:70;7481:157;;7597:4;:19;;7617:6;7597:27;;;;;;:::i;:::-;;;;;;;;;;;;;7625:1;7597:30;;;;;;;;-1:-1:-1;;;7597:30:1;;;;;;;;;;;;;;;;;;7589:38;;;;;;;;7597:30;;;;;;;7589:38;;;;;;;;;-1:-1:-1;;;;;7589:38:1;;;;;;;;;;;;;;;;;;;7597:30;7589:38;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;7589:38:1;;;-1:-1:-1;;7589:38:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7589:38:1;;;;;;;-1:-1:-1;7481:157:1;7466:3;;;;:::i;:::-;;;;7408:238;;;;7356:296;7672:12;;:33;;;;;7688:5;:17;;;7672:33;7657:99;;;;-1:-1:-1;;;7657:99:1;;9409:2:16;7657:99:1;;;9391:21:16;9448:2;9428:18;;;9421:30;9487:34;9467:18;;;9460:62;-1:-1:-1;;;9538:18:16;;;9531:33;9581:19;;7657:99:1;9381:225:16;7657:99:1;7770:5;7066:714;-1:-1:-1;;;;7066:714:1:o;5136:678:4:-;5296:31;5335:23;5375:99;5412:4;5424:10;:24;;;5456:12;5375:29;:99::i;:::-;5544:30;;;;5519:55;;;;-1:-1:-1;5544:30:4;5519:55;;:21;;:55;;5544:30;;5519:55;;;-1:-1:-1;;;;;5519:55:4;;:::i;:::-;;;-1:-1:-1;;;;;5519:55:4;;;;;;;;;;;;;;;;;;;;;5607:21;;5639:23;;;;5607:21;;;;;;-1:-1:-1;5639:27:4;;;-1:-1:-1;5635:175:4;;5686:60;5716:4;5722:9;5733:12;5686:29;:60::i;:::-;5780:23;;;;5755:48;;;;-1:-1:-1;5780:23:4;5755:48;;:21;;:48;;5780:23;;5755:48;;;-1:-1:-1;;;;;5755:48:4;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;5755:48:4;;;;;-1:-1:-1;;;;;5755:48:4;;;;;;5635:175;5136:678;;;;;;;:::o;196:470:3:-;301:7;343:2;326:13;:19;;;;318:71;;;;-1:-1:-1;;;318:71:3;;10922:2:16;318:71:3;;;10904:21:16;10961:2;10941:18;;;10934:30;11000:34;10980:18;;;10973:62;-1:-1:-1;;;11051:18:16;;;11044:37;11098:19;;318:71:3;10894:229:16;318:71:3;504:1;488:13;:17;;;484:105;;;563:17;579:1;563:13;:17;:::i;:::-;549:32;;557:2;549:32;:::i;:::-;522:60;;-1:-1:-1;;;;;522:23:3;;:60;:::i;484:105::-;642:17;646:13;642:1;:17;:::i;:::-;628:32;;636:2;628:32;:::i;:::-;601:60;;-1:-1:-1;;;;;601:23:3;;:60;:::i;5840:427:4:-;5957:36;6001:23;6041:57;6071:4;6077:6;6085:12;6041:29;:57::i;:::-;6136:21;;;;;-1:-1:-1;;;;;6136:21:4;;-1:-1:-1;6136:21:4;-1:-1:-1;6136:21:4;6164:66;;;;-1:-1:-1;;;6164:66:4;;11330:2:16;6164:66:4;;;11312:21:16;11369:2;11349:18;;;11342:30;-1:-1:-1;;;11388:18:16;;;11381:50;11448:18;;6164:66:4;11302:170:16;6164:66:4;6237:25;;-1:-1:-1;;;;;;6237:25:4;;;5840:427;;-1:-1:-1;;;5840:427:4:o;3681:613:8:-;3850:16;;;;3878:24;;;;3773:7;;3850:16;3773:7;4010:25;;:52;;;;;;-1:-1:-1;;;4010:52:8;;;;;;;;;;:163;;4149:10;:23;;;4132:41;;;;;;;4351:2:16;4322:15;;;;-1:-1:-1;;;;;;4318:45:16;4306:58;;4389:2;4380:12;;4296:102;4132:41:8;;;;;;;;;;;;;4010:163;;;4094:10;:22;;;4077:40;;;;;;;;:::i;:::-;;;;;;;;;;;;;4010:163;4185:44;4198:10;:30;;;-1:-1:-1;;;;;4185:44:8;:12;:44::i;:::-;4241:10;:30;;;3822:459;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;3803:486;;;;;;3790:499;;3681:613;;;:::o;421:219::-;540:4;629:6;-1:-1:-1;;;;;565:70:8;:60;579:34;608:4;579:28;:34::i;:::-;615:9;565:13;:60::i;:::-;-1:-1:-1;;;;;565:70:8;;;421:219;-1:-1:-1;;;;421:219:8:o;8061:179:1:-;8149:12;;:::i;:::-;-1:-1:-1;8178:57:1;;;;;;;;8184:4;8178:57;;;-1:-1:-1;8178:57:1;;;;;;;;;;;;;8223:2;8178:57;;;;;;;;;;;;;;;;8061:179::o;8334:181::-;8425:4;8507:1;8490:19;;;;;;;;:::i;:::-;;;;;;;;;;;;;8480:30;;;;;;8473:1;8456:19;;;;;;;;:::i;:::-;;;;;;;;;;;;;8446:30;;;;;;:64;8439:71;;8334:181;;;;:::o;10166:510:4:-;-1:-1:-1;;;;;10353:38:4;;;10298:15;10353:38;;;;;;;;;;;:52;;;;;;;;;;;10417:18;;;;10416:19;:68;;;;-1:-1:-1;10447:20:4;;;;-1:-1:-1;;;;;10447:20:4;10439:45;;10416:68;10412:239;;;10518:20;;;;:93;;-1:-1:-1;;;10518:93:4;;-1:-1:-1;;;;;6509:15:16;;;10518:93:4;;;6491:34:16;6561:15;;;6541:18;;;6534:43;10518:20:4;;;;:47;;6426:18:16;;10518:93:4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10494:117;;-1:-1:-1;;;;;;;10494:117:4;;;;;;10619:25;;;;-1:-1:-1;;;;;;10619:25:4;;;;10494:21;10619:25;;;10664:7;-1:-1:-1;10166:510:4;;;;;:::o;4486:776:8:-;4544:13;4725:4;4710:12;4755:59;4762:9;;4755:59;;4781:8;;;;:::i;:::-;;-1:-1:-1;4797:10:8;;-1:-1:-1;4805:2:8;4797:10;;:::i;:::-;;;4755:59;;;4832:1;4823:6;:10;4819:92;;;-1:-1:-1;4852:1:8;4819:92;4916:8;;;;:::i;:::-;;;;4956:20;4989:6;-1:-1:-1;;;;;4979:17:8;;;;;-1:-1:-1;;;4979:17:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4979:17:8;-1:-1:-1;4956:40:8;-1:-1:-1;5019:6:8;5002:228;5027:5;;5002:228;;5051:10;5060:1;5051:6;:10;:::i;:::-;5065:1;5051:15;5047:177;;;-1:-1:-1;;;5078:7:8;5086:5;5090:1;5086;:5;:::i;:::-;5078:14;;;;;;-1:-1:-1;;;5078:14:8;;;;;;;;;;;;:34;-1:-1:-1;;;;;5078:34:8;;;;;;;;;5047:177;;;5183:9;5190:2;5183:4;:9;:::i;:::-;5177:16;;:2;:16;:::i;:::-;5164:31;;5147:7;5155:5;5159:1;5155;:5;:::i;:::-;5147:14;;;;;;-1:-1:-1;;;5147:14:8;;;;;;;;;;;;:48;-1:-1:-1;;;;;5147:48:8;;;;;;;;-1:-1:-1;5205:10:8;5213:2;5205:10;;:::i;:::-;;;5047:177;5034:3;;;;:::i;:::-;;;;5002:228;;4360:265:6;4559:58;;-1:-1:-1;;;4559:58:6;;;4914:80:16;5010:12;;;5003:28;;;4429:7:6;;5047:12:16;;4559:58:6;4904:161:16;1064:1459:6;1142:7;1217:9;1236;1255:7;1470:9;:16;1490:2;1470:22;1466:1011;;;-1:-1:-1;;;1752:4:6;1737:20;;1731:27;1801:4;1786:20;;1780:27;1858:4;1843:20;;1837:27;1834:1;1829:36;1708:171;;;1899:9;:16;1919:2;1899:22;1895:582;;;-1:-1:-1;;;2186:4:6;2171:20;;2165:27;2235:4;2220:20;;2214:27;;-1:-1:-1;;;;;2263:75:6;;;2368:3;2364:12;2378:2;2360:21;2137:258;;;2425:41;;-1:-1:-1;;;2425:41:6;;7899:2:16;2425:41:6;;;7881:21:16;7938:2;7918:18;;;7911:30;7977:33;7957:18;;;7950:61;8028:18;;2425:41:6;7871:181:16;1895:582:6;2494:22;2502:4;2508:1;2511;2514;2494:7;:22::i;:::-;2487:29;1064:1459;-1:-1:-1;;;;;;1064:1459:6:o;2656:1414::-;2741:7;-1:-1:-1;;;;;3642:80:6;;;3634:127;;;;-1:-1:-1;;;3634:127:6;;8603:2:16;3634:127:6;;;8585:21:16;8642:2;8622:18;;;8615:30;8681:34;8661:18;;;8654:62;-1:-1:-1;;;8732:18:16;;;8725:32;8774:19;;3634:127:6;8575:224:16;3634:127:6;3779:1;:7;;3784:2;3779:7;:18;;;;3790:1;:7;;3795:2;3790:7;3779:18;3771:65;;;;-1:-1:-1;;;3771:65:6;;9006:2:16;3771:65:6;;;8988:21:16;9045:2;9025:18;;;9018:30;9084:34;9064:18;;;9057:62;-1:-1:-1;;;9135:18:16;;;9128:32;9177:19;;3771:65:6;8978:224:16;3771:65:6;3948:24;;;3931:14;3948:24;;;;;;;;;6815:25:16;;;6888:4;6876:17;;6856:18;;;6849:45;;;;6910:18;;;6903:34;;;6953:18;;;6946:34;;;3948:24:6;;6787:19:16;;3948:24:6;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3948:24:6;;-1:-1:-1;;3948:24:6;;;-1:-1:-1;;;;;;;3990:20:6;;3982:57;;;;-1:-1:-1;;;3982:57:6;;7193:2:16;3982:57:6;;;7175:21:16;7232:2;7212:18;;;7205:30;-1:-1:-1;;;7251:18:16;;;7244:54;7315:18;;3982:57:6;7165:174:16;3982:57:6;4057:6;2656:1414;-1:-1:-1;;;;;2656:1414:6:o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:134:16:-;82:20;;111:31;82:20;111:31;:::i;:::-;63:85;;;:::o;153:160::-;218:20;;274:13;;267:21;257:32;;247:2;;303:1;300;293:12;318:738;360:5;413:3;406:4;398:6;394:17;390:27;380:2;;435:5;428;421:20;380:2;462:20;;-1:-1:-1;;;;;531:10:16;;;528:2;;;544:18;;:::i;:::-;619:2;613:9;587:2;673:13;;-1:-1:-1;;669:22:16;;;693:2;665:31;661:40;649:53;;;717:18;;;737:22;;;714:46;711:2;;;763:18;;:::i;:::-;803:10;799:2;792:22;838:2;830:6;823:18;884:3;877:4;872:2;864:6;860:15;856:26;853:35;850:2;;;905:5;898;891:20;850:2;973;966:4;958:6;954:17;947:4;939:6;935:17;922:54;996:15;;;1013:4;992:26;985:41;;;;-1:-1:-1;1000:6:16;370:686;-1:-1:-1;;;370:686:16:o;1061:155::-;1141:20;;1190:1;1180:12;;1170:2;;1206:1;1203;1196:12;1221:173;1289:20;;-1:-1:-1;;;;;1338:31:16;;1328:42;;1318:2;;1384:1;1381;1374:12;1399:132;1466:20;;1495:30;1466:20;1495:30;:::i;1536:605::-;1692:6;1700;1708;1716;1769:3;1757:9;1748:7;1744:23;1740:33;1737:2;;;1791:6;1783;1776:22;1737:2;1835:9;1822:23;1854:31;1879:5;1854:31;:::i;:::-;1904:5;-1:-1:-1;1961:2:16;1946:18;;1933:32;1974:33;1933:32;1974:33;:::i;:::-;1727:414;;2026:7;;-1:-1:-1;;;;2080:2:16;2065:18;;2052:32;;2131:2;2116:18;2103:32;;1727:414::o;2146:1762::-;2370:6;2378;2386;2394;2402;2410;2463:3;2451:9;2442:7;2438:23;2434:33;2431:2;;;2485:6;2477;2470:22;2431:2;2517:23;;-1:-1:-1;;;;;2589:14:16;;;2586:2;;;2621:6;2613;2606:22;2586:2;2649:22;;;;2705:6;2687:16;;;2683:29;2680:2;;;2730:6;2722;2715:22;2680:2;2761:17;;:::i;:::-;2801:34;2832:2;2801:34;:::i;:::-;2794:5;2787:49;2868:31;2895:2;2891;2887:11;2868:31;:::i;:::-;2863:2;2856:5;2852:14;2845:55;2932:31;2959:2;2955;2951:11;2932:31;:::i;:::-;2927:2;2920:5;2916:14;2909:55;3010:2;3006;3002:11;2989:25;3039:2;3029:8;3026:16;3023:2;;;3060:6;3052;3045:22;3023:2;3101:44;3137:7;3126:8;3122:2;3118:17;3101:44;:::i;:::-;3096:2;3089:5;3085:14;3078:68;;3179:32;3206:3;3202:2;3198:12;3179:32;:::i;:::-;3173:3;3166:5;3162:15;3155:57;3245:31;3271:3;3267:2;3263:12;3245:31;:::i;:::-;3239:3;3232:5;3228:15;3221:56;3310:31;3336:3;3332:2;3328:12;3310:31;:::i;:::-;3304:3;3297:5;3293:15;3286:56;3375:29;3399:3;3395:2;3391:12;3375:29;:::i;:::-;3369:3;3362:5;3358:15;3351:54;3424:3;3473:2;3469;3465:11;3452:25;3502:2;3492:8;3489:16;3486:2;;;3523:6;3515;3508:22;3486:2;3564:44;3600:7;3589:8;3585:2;3581:17;3564:44;:::i;:::-;3559:2;3552:5;3548:14;3541:68;;;3628:5;3618:15;;;;;3652:38;3686:2;3675:9;3671:18;3652:38;:::i;:::-;3642:48;;3709:38;3743:2;3732:9;3728:18;3709:38;:::i;:::-;3699:48;;3794:2;3783:9;3779:18;3766:32;3756:42;;3845:3;3834:9;3830:19;3817:33;3807:43;;3897:3;3886:9;3882:19;3869:33;3859:43;;2421:1487;;;;;;;;:::o;3913:259::-;3982:6;4035:2;4023:9;4014:7;4010:23;4006:32;4003:2;;;4056:6;4048;4041:22;4003:2;4093:9;4087:16;4112:30;4136:5;4112:30;:::i;4403:276::-;4534:3;4572:6;4566:13;4588:53;4634:6;4629:3;4622:4;4614:6;4610:17;4588:53;:::i;:::-;4657:16;;;;;4542:137;-1:-1:-1;;4542:137:16:o;5070:808::-;5405:3;5375:16;;;-1:-1:-1;;;;;;5371:47:16;5359:60;;5482:2;5453:15;;;-1:-1:-1;;;;;;5449:45:16;5444:2;5435:12;;5428:67;5518:13;;-1:-1:-1;;5540:62:16;5518:13;5590:2;5581:12;;5574:4;5562:17;;5540:62;:::i;:::-;5662:13;;5621:16;;;;5684:63;5662:13;5733:2;5725:11;;5718:4;5706:17;;5684:63;:::i;:::-;5828:14;;5821:22;5816:3;5812:32;5766:17;;5807:2;5799:11;;5792:53;;;;-1:-1:-1;;5869:2:16;5861:11;;;-1:-1:-1;;;;5349:529:16:o;5883:391::-;-1:-1:-1;;;;;6157:15:16;;;6139:34;;6209:15;;;;6204:2;6189:18;;6182:43;6256:2;6241:18;;6234:34;;;;6089:2;6074:18;;6056:218::o;11690:657::-;11957:1;11953;11949:2;11945:10;11941:18;11933:6;11929:31;11918:9;11911:50;11997:6;11992:2;11981:9;11977:18;11970:34;12069:1;12065;12060:3;12056:11;12052:19;12044:6;12040:32;12035:2;12024:9;12020:18;12013:60;12109:3;12104:2;12093:9;12089:18;12082:31;11892:4;12142:6;12136:13;12186:6;12180:3;12169:9;12165:19;12158:35;12202:67;12262:6;12256:3;12245:9;12241:19;12236:2;12228:6;12224:15;12202:67;:::i;:::-;12330:2;12309:15;-1:-1:-1;;12305:29:16;12290:45;;;;12337:3;12286:55;;11901:446;-1:-1:-1;;;;;11901:446:16:o;12352:250::-;12419:2;12413:9;12461:6;12449:19;;-1:-1:-1;;;;;12483:34:16;;12519:22;;;12480:62;12477:2;;;12545:18;;:::i;:::-;12581:2;12574:22;12393:209;:::o;12607:128::-;12647:3;12678:1;12674:6;12671:1;12668:13;12665:2;;;12684:18;;:::i;:::-;-1:-1:-1;12720:9:16;;12655:80::o;12740:236::-;12779:3;-1:-1:-1;;;;;12845:10:16;;;12875;;;12905:12;;;12897:21;;12894:2;;;12921:18;;:::i;:::-;12957:13;;12787:189;-1:-1:-1;;;;12787:189:16:o;12981:201::-;13021:1;-1:-1:-1;;;;;13086:10:16;;;;13105:2;;13122:18;;:::i;:::-;13160:10;;13156:20;;;;;13027:155;-1:-1:-1;;13027:155:16:o;13187:120::-;13227:1;13253;13243:2;;13258:18;;:::i;:::-;-1:-1:-1;13292:9:16;;13233:74::o;13312:199::-;13351:1;-1:-1:-1;;;;;13415:10:16;;;;13434:2;;13451:18;;:::i;13516:422::-;13605:1;13648:5;13605:1;13662:270;13683:7;13673:8;13670:21;13662:270;;;13742:4;13738:1;13734:6;13730:17;13724:4;13721:27;13718:2;;;13751:18;;:::i;:::-;13801:7;13791:8;13787:22;13784:2;;;13821:16;;;;13784:2;13900:22;;;;13860:15;;;;13662:270;;;13666:3;13580:358;;;;;:::o;13943:140::-;14001:5;14030:47;14071:4;14061:8;14057:19;14051:4;14137:5;14167:8;14157:2;;-1:-1:-1;14208:1:16;14222:5;;14157:2;14256:4;14246:2;;-1:-1:-1;14293:1:16;14307:5;;14246:2;14338:4;14356:1;14351:59;;;;14424:1;14419:130;;;;14331:218;;14351:59;14381:1;14372:10;;14395:5;;;14419:130;14456:3;14446:8;14443:17;14440:2;;;14463:18;;:::i;:::-;-1:-1:-1;;14519:1:16;14505:16;;14534:5;;14331:218;;14633:2;14623:8;14620:16;14614:3;14608:4;14605:13;14601:36;14595:2;14585:8;14582:16;14577:2;14571:4;14568:12;14564:35;14561:77;14558:2;;;-1:-1:-1;14670:19:16;;;14702:5;;14558:2;14749:34;14774:8;14768:4;14749:34;:::i;:::-;14819:6;14815:1;14811:6;14807:19;14798:7;14795:32;14792:2;;;14830:18;;:::i;:::-;14868:20;;14147:747;-1:-1:-1;;;14147:747:16:o;14899:168::-;14939:7;15005:1;15001;14997:6;14993:14;14990:1;14987:21;14982:1;14975:9;14968:17;14964:45;14961:2;;;15012:18;;:::i;:::-;-1:-1:-1;15052:9:16;;14951:116::o;15072:270::-;15111:7;-1:-1:-1;;;;;15181:10:16;;;15211;;;15244:11;;15237:19;15266:12;;;15258:21;;15233:47;15230:2;;;15283:18;;:::i;:::-;15323:13;;15123:219;-1:-1:-1;;;;15123:219:16:o;15347:125::-;15387:4;15415:1;15412;15409:8;15406:2;;;15420:18;;:::i;:::-;-1:-1:-1;15457:9:16;;15396:76::o;15477:229::-;15516:4;-1:-1:-1;;;;;15613:10:16;;;;15583;;15635:12;;;15632:2;;;15650:18;;:::i;:::-;15687:13;;15525:181;-1:-1:-1;;;15525:181:16:o;15711:195::-;15749:4;15786;15783:1;15779:12;15818:4;15815:1;15811:12;15843:3;15838;15835:12;15832:2;;;15850:18;;:::i;:::-;15887:13;;;15758:148;-1:-1:-1;;;15758:148:16:o;15911:258::-;15983:1;15993:113;16007:6;16004:1;16001:13;15993:113;;;16083:11;;;16077:18;16064:11;;;16057:39;16029:2;16022:10;15993:113;;;16124:6;16121:1;16118:13;16115:2;;;16159:1;16150:6;16145:3;16141:16;16134:27;16115:2;;15964:205;;;:::o;16174:136::-;16213:3;16241:5;16231:2;;16250:18;;:::i;:::-;-1:-1:-1;;;16286:18:16;;16221:89::o;16315:380::-;16394:1;16390:12;;;;16437;;;16458:2;;16512:4;16504:6;16500:17;16490:27;;16458:2;16565;16557:6;16554:14;16534:18;16531:38;16528:2;;;16611:10;16606:3;16602:20;16599:1;16592:31;16646:4;16643:1;16636:15;16674:4;16671:1;16664:15;16528:2;;16370:325;;;:::o;16700:135::-;16739:3;-1:-1:-1;;16760:17:16;;16757:2;;;16780:18;;:::i;:::-;-1:-1:-1;16827:1:16;16816:13;;16747:88::o;16840:175::-;16877:3;16921:4;16914:5;16910:16;16950:4;16941:7;16938:17;16935:2;;;16958:18;;:::i;:::-;17007:1;16994:15;;16885:130;-1:-1:-1;;16885:130:16:o;17020:112::-;17052:1;17078;17068:2;;17083:18;;:::i;:::-;-1:-1:-1;17117:9:16;;17058:74::o;17137:127::-;17198:10;17193:3;17189:20;17186:1;17179:31;17229:4;17226:1;17219:15;17253:4;17250:1;17243:15;17269:127;17330:10;17325:3;17321:20;17318:1;17311:31;17361:4;17358:1;17351:15;17385:4;17382:1;17375:15;17401:127;17462:10;17457:3;17453:20;17450:1;17443:31;17493:4;17490:1;17483:15;17517:4;17514:1;17507:15;17533:131;-1:-1:-1;;;;;17608:31:16;;17598:42;;17588:2;;17654:1;17651;17644:12;17588:2;17578:86;:::o;17669:129::-;-1:-1:-1;;;;;17743:30:16;;17733:41;;17723:2;;17788:1;17785;17778:12
Swarm Source
ipfs://7fac73239b67f9206f701f29abd0bc658c1c450db9dafc60f6127dc32b06b03c
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.