From 2fd26587e5a2a8bb622bf83ee20ea38a7fdb89d4 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 31 Jan 2020 11:40:31 -0500 Subject: [PATCH 01/18] Cherry-pick changes from `feat/dev-utils/dydx-bridge-validation` --- contracts/erc20/CHANGELOG.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/erc20/CHANGELOG.json b/contracts/erc20/CHANGELOG.json index e4517eac60..9c68812dfe 100644 --- a/contracts/erc20/CHANGELOG.json +++ b/contracts/erc20/CHANGELOG.json @@ -13,11 +13,11 @@ "changes": [ { "note": "Add `allowance()` and `balanceOf()` to `LibERC20Token`", - "pr": 2464 + "pr": 2462 }, { "note": "Fix broken tests", - "pr": 2456 + "pr": 2462 } ], "timestamp": 1581204851 From b4cefc64b493b8aba748d76c4c4b93d5a33a1504 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 31 Jan 2020 14:38:42 -0500 Subject: [PATCH 02/18] Cherry pick library linking code from #2456 --- contracts/dev-utils/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/dev-utils/src/index.ts b/contracts/dev-utils/src/index.ts index 91a089b69a..86d122eb2e 100644 --- a/contracts/dev-utils/src/index.ts +++ b/contracts/dev-utils/src/index.ts @@ -1,5 +1,5 @@ export { artifacts } from './artifacts'; -export { DevUtilsContract } from './wrappers'; +export { DevUtilsContract, LibAssetDataContract, LibTransactionDecoderContract } from './wrappers'; export { ContractArtifact, ContractChains, From d89243a0d3df811680e2ffca815d0be9b306716d Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 31 Jan 2020 15:56:08 -0500 Subject: [PATCH 03/18] Cherry pick `DevUtils` refactor code from #2456 --- contracts/dev-utils/src/index.ts | 2 +- contracts/erc20/CHANGELOG.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/dev-utils/src/index.ts b/contracts/dev-utils/src/index.ts index 86d122eb2e..91a089b69a 100644 --- a/contracts/dev-utils/src/index.ts +++ b/contracts/dev-utils/src/index.ts @@ -1,5 +1,5 @@ export { artifacts } from './artifacts'; -export { DevUtilsContract, LibAssetDataContract, LibTransactionDecoderContract } from './wrappers'; +export { DevUtilsContract } from './wrappers'; export { ContractArtifact, ContractChains, diff --git a/contracts/erc20/CHANGELOG.json b/contracts/erc20/CHANGELOG.json index 9c68812dfe..e4517eac60 100644 --- a/contracts/erc20/CHANGELOG.json +++ b/contracts/erc20/CHANGELOG.json @@ -13,11 +13,11 @@ "changes": [ { "note": "Add `allowance()` and `balanceOf()` to `LibERC20Token`", - "pr": 2462 + "pr": 2464 }, { "note": "Fix broken tests", - "pr": 2462 + "pr": 2456 } ], "timestamp": 1581204851 From 9b3781abf1f09570ce73669c031d97b4c6caba7f Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 31 Jan 2020 21:02:58 -0500 Subject: [PATCH 04/18] Cherry pick dydx validation from #2456 --- contracts/asset-proxy/CHANGELOG.json | 8 + .../contracts/src/bridges/DydxBridge.sol | 4 +- .../contracts/src/interfaces/IDydx.sol | 82 +- .../contracts/src/interfaces/IDydxBridge.sol | 4 +- .../contracts/test/TestDydxBridge.sol | 51 +- .../asset-proxy/src/dydx_bridge_encoder.ts | 4 +- contracts/asset-proxy/test/dydx_bridge.ts | 6 +- contracts/dev-utils/CHANGELOG.json | 4 + .../dev-utils/contracts/src/Addresses.sol | 5 +- .../dev-utils/contracts/src/AssetBalance.sol | 16 +- .../dev-utils/contracts/src/DevUtils.sol | 6 +- .../contracts/src/LibDydxBalance.sol | 483 +++++++ .../dev-utils/contracts/test/TestDydx.sol | 162 +++ .../contracts/test/TestLibDydxBalance.sol | 116 ++ contracts/dev-utils/package.json | 6 +- contracts/dev-utils/src/artifacts.ts | 2 + contracts/dev-utils/src/wrappers.ts | 1 + contracts/dev-utils/test/artifacts.ts | 6 + .../dev-utils/test/lib_dydx_balance_test.ts | 1166 +++++++++++++++++ contracts/dev-utils/test/wrappers.ts | 3 + contracts/dev-utils/tsconfig.json | 6 +- contracts/integrations/CHANGELOG.json | 4 + .../test/bridges/dydx_bridge_mainnet_test.ts | 8 +- .../test/dev-utils/dev_utils_mainnet_test.ts | 1 + .../test/dev-utils/get_order_hash.ts | 1 + .../test/dev-utils/lib_asset_data.ts | 1 + .../test/dev-utils/lib_transaction_decoder.ts | 1 + .../test/exchange/fill_dydx_order_test.ts | 4 +- .../test/framework/deployment_manager.ts | 1 + contracts/utils/CHANGELOG.json | 4 + .../utils/contracts/src/LibFractions.sol | 96 +- packages/abi-gen/CHANGELOG.json | 4 + packages/contract-addresses/CHANGELOG.json | 2 +- packages/contract-addresses/addresses.json | 10 +- packages/contract-artifacts/CHANGELOG.json | 2 +- packages/migrations/CHANGELOG.json | 4 + packages/migrations/src/migration.ts | 1 + packages/migrations/src/testnet_migrations.ts | 3 +- 38 files changed, 2252 insertions(+), 36 deletions(-) create mode 100644 contracts/dev-utils/contracts/src/LibDydxBalance.sol create mode 100644 contracts/dev-utils/contracts/test/TestDydx.sol create mode 100644 contracts/dev-utils/contracts/test/TestLibDydxBalance.sol create mode 100644 contracts/dev-utils/test/lib_dydx_balance_test.ts diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index 66721bc89d..874141201b 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -11,6 +11,14 @@ { "version": "3.2.0", "changes": [ + { + "note": "Add more types and functions to `IDydx`", + "pr": 2466 + }, + { + "note": "Rename `DydxBrigeAction.accountId` to `DydxBridgeAction.accountIdx`", + "pr": 2466 + }, { "note": "Fix broken tests.", "pr": 2462 diff --git a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol index 1a5bf1bdd4..70c97f268f 100644 --- a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol +++ b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol @@ -191,7 +191,7 @@ contract DydxBridge is depositAction = IDydx.ActionArgs({ actionType: IDydx.ActionType.Deposit, // deposit tokens. amount: dydxAmount, // amount to deposit. - accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`. + accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`. primaryMarketId: bridgeAction.marketId, // indicates which token to deposit. otherAddress: depositFrom, // deposit from the account owner. // unused parameters @@ -229,7 +229,7 @@ contract DydxBridge is withdrawAction = IDydx.ActionArgs({ actionType: IDydx.ActionType.Withdraw, // withdraw tokens. amount: amountToWithdraw, // amount to withdraw. - accountId: bridgeAction.accountId, // index in the `accounts` when calling `operate`. + accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`. primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw. otherAddress: withdrawTo, // withdraw tokens to this address. // unused parameters diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol index 56e5e9da36..d784bde2cc 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol @@ -45,7 +45,7 @@ interface IDydx { /// parsed into before being processed. struct ActionArgs { ActionType actionType; - uint256 accountId; + uint256 accountIdx; AssetAmount amount; uint256 primaryMarketId; uint256 secondaryMarketId; @@ -71,6 +71,31 @@ interface IDydx { uint256 value; } + struct D256 { + uint256 value; + } + + struct Value { + uint256 value; + } + + struct Price { + uint256 value; + } + + /// @dev The global risk parameters that govern the health and security of the system + struct RiskParams { + // Required ratio of over-collateralization + D256 marginRatio; + // Percentage penalty incurred by liquidated accounts + D256 liquidationSpread; + // Percentage of the borrower's interest fee that gets passed to the suppliers + D256 earningsRate; + // The minimum absolute borrow value of an account + // There must be sufficient incentivize to liquidate undercollateralized accounts + Value minBorrowedValue; + } + /// @dev The main entry-point to Solo that allows users and contracts to manage accounts. /// Take one or more actions on one or more accounts. The msg.sender must be the owner or /// operator of all accounts except for those being liquidated, vaporized, or traded with. @@ -86,4 +111,59 @@ interface IDydx { ActionArgs[] calldata actions ) external; + + /// @dev Return true if a particular address is approved as an operator for an owner's accounts. + /// Approved operators can act on the accounts of the owner as if it were the operator's own. + /// @param owner The owner of the accounts + /// @param operator The possible operator + /// @return isLocalOperator True if operator is approved for owner's accounts + function getIsLocalOperator( + address owner, + address operator + ) + external + view + returns (bool isLocalOperator); + + /// @dev Get the ERC20 token address for a market. + /// @param marketId The market to query + /// @return tokenAddress The token address + function getMarketTokenAddress( + uint256 marketId + ) + external + view + returns (address tokenAddress); + + /// @dev Get all risk parameters in a single struct. + /// @return riskParams All global risk parameters + function getRiskParams() + external + view + returns (RiskParams memory riskParams); + + /// @dev Get the price of the token for a market. + /// @param marketId The market to query + /// @return price The price of each atomic unit of the token + function getMarketPrice( + uint256 marketId + ) + external + view + returns (Price memory price); + + /// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium + /// of each market. Supplied values are divided by (1 + marginPremium) for each market and + /// borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these + /// adjusted values gives the margin-ratio of the account which will be compared to the global + /// margin-ratio when determining if the account can be liquidated. + /// @param account The account to query + /// @return supplyValue The supplied value of the account (adjusted for marginPremium) + /// @return borrowValue The borrowed value of the account (adjusted for marginPremium) + function getAdjustedAccountValues( + AccountInfo calldata account + ) + external + view + returns (Value memory supplyValue, Value memory borrowValue); } diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol index 1b69179f49..779bf72fdc 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol @@ -29,7 +29,7 @@ interface IDydxBridge { struct BridgeAction { BridgeActionType actionType; // Action to run on dydx account. - uint256 accountId; // Index in `BridgeData.accountNumbers` for this action. + uint256 accountIdx; // Index in `BridgeData.accountNumbers` for this action. uint256 marketId; // Market to operate on. uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). @@ -39,4 +39,4 @@ interface IDydxBridge { uint256[] accountNumbers; // Account number used to identify the owner's specific account. BridgeAction[] actions; // Actions to carry out on the owner's accounts. } -} \ No newline at end of file +} diff --git a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol index 31dcc5359f..895e31231b 100644 --- a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol +++ b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol @@ -23,6 +23,7 @@ import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "../src/bridges/DydxBridge.sol"; +// solhint-disable no-empty-blocks contract TestDydxBridgeToken { uint256 private constant INIT_HOLDER_BALANCE = 10 * 10**18; // 10 tokens @@ -79,7 +80,7 @@ contract TestDydxBridge is event OperateAction( ActionType actionType, - uint256 accountId, + uint256 accountIdx, bool amountSign, AssetDenomination amountDenomination, AssetReference amountRef, @@ -120,7 +121,7 @@ contract TestDydxBridge is for (uint i = 0; i < actions.length; ++i) { emit OperateAction( actions[i].actionType, - actions[i].accountId, + actions[i].accountIdx, actions[i].amount.sign, actions[i].amount.denomination, actions[i].amount.ref, @@ -171,6 +172,50 @@ contract TestDydxBridge is return _testTokenAddress; } + /// @dev Unused. + function getIsLocalOperator( + address owner, + address operator + ) + external + view + returns (bool isLocalOperator) + {} + + /// @dev Unused. + function getMarketTokenAddress( + uint256 marketId + ) + external + view + returns (address tokenAddress) + {} + + /// @dev Unused. + function getRiskParams() + external + view + returns (RiskParams memory riskParams) + {} + + /// @dev Unsused. + function getMarketPrice( + uint256 marketId + ) + external + view + returns (Price memory price) + {} + + /// @dev Unused. + function getAdjustedAccountValues( + AccountInfo calldata account + ) + external + view + returns (Value memory supplyValue, Value memory borrowValue) + {} + /// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address. function _getDydxAddress() internal @@ -188,4 +233,4 @@ contract TestDydxBridge is { return msg.sender == ALWAYS_REVERT_ADDRESS ? address(0) : msg.sender; } -} \ No newline at end of file +} diff --git a/contracts/asset-proxy/src/dydx_bridge_encoder.ts b/contracts/asset-proxy/src/dydx_bridge_encoder.ts index 8d07ec65ab..63dcade973 100644 --- a/contracts/asset-proxy/src/dydx_bridge_encoder.ts +++ b/contracts/asset-proxy/src/dydx_bridge_encoder.ts @@ -7,7 +7,7 @@ export enum DydxBridgeActionType { export interface DydxBridgeAction { actionType: DydxBridgeActionType; - accountId: BigNumber; + accountIdx: BigNumber; marketId: BigNumber; conversionRateNumerator: BigNumber; conversionRateDenominator: BigNumber; @@ -29,7 +29,7 @@ export const dydxBridgeDataEncoder = AbiEncoder.create([ type: 'tuple[]', components: [ { name: 'actionType', type: 'uint8' }, - { name: 'accountId', type: 'uint256' }, + { name: 'accountIdx', type: 'uint256' }, { name: 'marketId', type: 'uint256' }, { name: 'conversionRateNumerator', type: 'uint256' }, { name: 'conversionRateDenominator', type: 'uint256' }, diff --git a/contracts/asset-proxy/test/dydx_bridge.ts b/contracts/asset-proxy/test/dydx_bridge.ts index 329291b0ca..3377ddfce4 100644 --- a/contracts/asset-proxy/test/dydx_bridge.ts +++ b/contracts/asset-proxy/test/dydx_bridge.ts @@ -17,14 +17,14 @@ blockchainTests.resets('DydxBridge unit tests', env => { const notAuthorized = '0x0000000000000000000000000000000000000001'; const defaultDepositAction = { actionType: DydxBridgeActionType.Deposit, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId, conversionRateNumerator: constants.ZERO_AMOUNT, conversionRateDenominator: constants.ZERO_AMOUNT, }; const defaultWithdrawAction = { actionType: DydxBridgeActionType.Withdraw, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId, conversionRateNumerator: constants.ZERO_AMOUNT, conversionRateDenominator: constants.ZERO_AMOUNT, @@ -118,7 +118,7 @@ blockchainTests.resets('DydxBridge unit tests', env => { for (const action of bridgeData.actions) { expectedOperateActionEvents.push({ actionType: action.actionType as number, - accountId: action.accountId, + accountIdx: action.accountIdx, amountSign: action.actionType === DydxBridgeActionType.Deposit ? true : false, amountDenomination: weiDenomination, amountRef: deltaAmountRef, diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index d04eb08985..0e8f288e99 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -18,6 +18,10 @@ { "note": "Remove `LibTransactionDecoder` export", "pr": 2464 + }, + { + "note": "Add `DydxBridge` order validation", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/dev-utils/contracts/src/Addresses.sol b/contracts/dev-utils/contracts/src/Addresses.sol index 3cd41a329c..12e58284fb 100644 --- a/contracts/dev-utils/contracts/src/Addresses.sol +++ b/contracts/dev-utils/contracts/src/Addresses.sol @@ -34,15 +34,18 @@ contract Addresses is address public erc1155ProxyAddress; address public staticCallProxyAddress; address public chaiBridgeAddress; + address public dydxBridgeAddress; constructor ( address exchange_, - address chaiBridge_ + address chaiBridge_, + address dydxBridge_ ) public { exchangeAddress = exchange_; chaiBridgeAddress = chaiBridge_; + dydxBridgeAddress = dydxBridge_; erc20ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC20Token.selector); erc721ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC721Token.selector); erc1155ProxyAddress = IExchange(exchange_).getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector); diff --git a/contracts/dev-utils/contracts/src/AssetBalance.sol b/contracts/dev-utils/contracts/src/AssetBalance.sol index cc72becb46..45172c757e 100644 --- a/contracts/dev-utils/contracts/src/AssetBalance.sol +++ b/contracts/dev-utils/contracts/src/AssetBalance.sol @@ -28,7 +28,7 @@ import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "./Addresses.sol"; -import "./LibAssetData.sol"; +import "./LibDydxBalance.sol"; contract AssetBalance is @@ -274,6 +274,9 @@ contract AssetBalance is uint256 chaiAllowance = LibERC20Token.allowance(_getChaiAddress(), ownerAddress, chaiBridgeAddress); // Dai allowance is unlimited if Chai allowance is unlimited allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance); + } else if (bridgeAddress == dydxBridgeAddress) { + // Dydx bridges always have infinite allowance. + allowance = _MAX_UINT256; } // Allowance will be 0 if bridge is not supported } @@ -366,6 +369,17 @@ contract AssetBalance is if (order.makerAssetData.length < 4) { return (0, 0); } + bytes4 assetProxyId = order.makerAssetData.readBytes4(0); + // Handle dydx bridge assets. + if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) { + (, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); + if (bridgeAddress == dydxBridgeAddress) { + return ( + LibDydxBalance.getDydxMakerBalance(order, dydxBridgeAddress), + _MAX_UINT256 + ); + } + } return ( getBalance(order.makerAddress, order.makerAssetData), getAssetProxyAllowance(order.makerAddress, order.makerAssetData) diff --git a/contracts/dev-utils/contracts/src/DevUtils.sol b/contracts/dev-utils/contracts/src/DevUtils.sol index 45ce184646..b275b3ccb1 100644 --- a/contracts/dev-utils/contracts/src/DevUtils.sol +++ b/contracts/dev-utils/contracts/src/DevUtils.sol @@ -40,12 +40,14 @@ contract DevUtils is { constructor ( address exchange_, - address chaiBridge_ + address chaiBridge_, + address dydxBridge_ ) public Addresses( exchange_, - chaiBridge_ + chaiBridge_, + dydxBridge_ ) LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants {} diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol new file mode 100644 index 0000000000..dac436f08d --- /dev/null +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -0,0 +1,483 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydxBridge.sol"; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-utils/contracts/src/LibBytes.sol"; +import "@0x/contracts-utils/contracts/src/LibFractions.sol"; +import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; +import "./LibAssetData.sol"; + + +// solhint-disable separate-by-one-line-in-contract +library LibDydxBalance { + + using LibBytes for bytes; + using LibSafeMath for uint256; + + /// @dev Decimal places for dydx value quantities. + uint256 private constant DYDX_UNITS_DECIMALS = 18; + /// @dev Base units for dydx value quantities. + uint256 private constant DYDX_UNITS_BASE = 10 ** DYDX_UNITS_DECIMALS; + + /// @dev A fraction/rate. + struct Fraction { + uint256 n; + uint256 d; + } + + /// @dev Structure that holds all pertinent info needed to perform a balance + /// check. + struct BalanceCheckInfo { + IDydx dydx; + address bridgeAddress; + address makerAddress; + address makerTokenAddress; + address takerTokenAddress; + uint256 makerAssetAmount; + uint256 takerAssetAmount; + uint256[] accounts; + IDydxBridge.BridgeAction[] actions; + } + + /// @dev Get the maker asset balance of an order with a `DydxBridge` maker asset. + /// @param order An order with a dydx maker asset. + /// @param dydx The address of the dydx contract. + /// @return balance The maker asset balance. + function getDydxMakerBalance(LibOrder.Order memory order, address dydx) + public + view + returns (uint256 balance) + { + BalanceCheckInfo memory info = _getBalanceCheckInfo(order, dydx); + // The Dydx bridge must be an operator for the maker. + if (!info.dydx.getIsLocalOperator(info.makerAddress, info.bridgeAddress)) { + return 0; + } + // Actions must be well-formed. + if (!_areActionsWellFormed(info)) { + return 0; + } + // If the rate we withdraw maker tokens is < 1, the asset proxy will + // throw because we will always transfer less maker tokens than asked. + if (_ltf(_getMakerTokenWithdrawRate(info), Fraction(1, 1))) { + return 0; + } + // The maker balance is the smaller of: + return LibSafeMath.min256( + // How many times we can execute all the deposit actions. + _getDepositableMakerAmount(info), + // How many times we can execute all the actions before the an + // account becomes undercollateralized. + _getSolventMakerAmount(info) + ); + } + + /// @dev Checks that: + /// 1. Actions are arranged as [...deposits, withdraw]. + /// 2. There is only one deposit for each market ID. + /// 3. Every action has a valid account index. + /// 4. There is exactly one withdraw at the end and it is for the + /// maker token. + /// @param info State from `_getBalanceCheckInfo()`. + /// @return areWellFormed Whether the actions are well-formed. + function _areActionsWellFormed(BalanceCheckInfo memory info) + internal + view + returns (bool areWellFormed) + { + if (info.actions.length == 0) { + return false; + } + uint256 depositCount = 0; + // Count the number of deposits. + for (; depositCount < info.actions.length; ++depositCount) { + IDydxBridge.BridgeAction memory action = info.actions[depositCount]; + if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { + break; + } + // Search all prior actions for the same market ID. + uint256 marketId = action.marketId; + for (uint256 j = 0; j < depositCount; ++j) { + if (info.actions[j].marketId == marketId) { + // Market ID is not unique. + return false; + } + } + // Check that the account index is within the valid range. + if (action.accountIdx >= info.accounts.length) { + return false; + } + } + // There must be exactly one withdraw action at the end. + if (depositCount + 1 != info.actions.length) { + return false; + } + IDydxBridge.BridgeAction memory withdraw = info.actions[depositCount]; + if (withdraw.actionType != IDydxBridge.BridgeActionType.Withdraw) { + return false; + } + // And it must be for the maker token. + if (info.dydx.getMarketTokenAddress(withdraw.marketId) != info.makerTokenAddress) { + return false; + } + // Check the account index. + return withdraw.accountIdx < info.accounts.length; + } + + /// @dev Returns the rate at which we withdraw maker tokens. + /// @param info State from `_getBalanceCheckInfo()`. + /// @return makerTokenWithdrawRate Maker token withdraw rate. + function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info) + internal + pure + returns (Fraction memory makerTokenWithdrawRate) + { + // The last action is always a withdraw for the maker token. + IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; + return _getActionRate(withdraw); + } + + /// @dev Get how much maker asset we can transfer before a deposit fails. + /// @param info State from `_getBalanceCheckInfo()`. + function _getDepositableMakerAmount(BalanceCheckInfo memory info) + internal + view + returns (uint256 depositableMakerAmount) + { + depositableMakerAmount = uint256(-1); + // The conversion rate from maker -> taker. + Fraction memory makerToTakerRate = Fraction( + info.takerAssetAmount, + info.makerAssetAmount + ); + // Take the minimum maker amount from all deposits. + for (uint256 i = 0; i < info.actions.length; ++i) { + IDydxBridge.BridgeAction memory action = info.actions[i]; + // Only looking at deposit actions. + if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { + continue; + } + Fraction memory depositRate = _getActionRate(action); + // Taker tokens will be transferred to the maker for every fill, so + // we reduce the effective deposit rate if we're depositing the taker + // token. + address depositToken = info.dydx.getMarketTokenAddress(action.marketId); + if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { + // `depositRate = max(0, depositRate - makerToTakerRate)` + if (_ltf(makerToTakerRate, depositRate)) { + depositRate = _subf(depositRate, makerToTakerRate); + } else { + depositRate = Fraction(0, 1); + } + } + // If the deposit rate is > 0, we are limited by the transferrable + // token balance of the maker. + if (_gtf(depositRate, Fraction(0, 1))) { + uint256 supply = _getTransferabeTokenAmount( + depositToken, + info.makerAddress, + address(info.dydx) + ); + depositableMakerAmount = LibSafeMath.min256( + depositableMakerAmount, + LibMath.getPartialAmountFloor( + depositRate.d, + depositRate.n, + supply + ) + ); + } + } + } + + /// @dev Get how much maker asset we can transfer before an account + /// becomes insolvent. + /// @param info State from `_getBalanceCheckInfo()`. + function _getSolventMakerAmount(BalanceCheckInfo memory info) + internal + view + returns (uint256 solventMakerAmount) + { + solventMakerAmount = uint256(-1); + assert(info.actions.length >= 1); + IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; + assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw); + Fraction memory minCr = _getMinimumCollateralizationRatio(info.dydx); + // CR < 1 will cause math underflows. + require(_gtef(minCr, Fraction(1, 1)), "DevUtils/MIN_CR_MUST_BE_GTE_ONE"); + // Loop through the accounts. + for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) { + (uint256 supplyValue, uint256 borrowValue) = + _getAccountValues(info, info.accounts[accountIdx]); + // All accounts must currently be solvent. + if (borrowValue != 0 && _ltf(Fraction(supplyValue, borrowValue), minCr)) { + return 0; + } + // If this is the same account used to in the withdraw/borrow action, + // compute the maker amount at which it will become insolvent. + if (accountIdx != withdraw.accountIdx) { + continue; + } + // Compute the deposit/collateralization rate, which is the rate at + // which (USD) value is added to the account across all markets. + Fraction memory dd = Fraction(0, 1); + for (uint256 i = 0; i < info.actions.length - 1; ++i) { + IDydxBridge.BridgeAction memory deposit = info.actions[i]; + assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit); + if (deposit.accountIdx == accountIdx) { + dd = _addf( + dd, + _toQuoteValue( + info.dydx, + deposit.marketId, + _getActionRate(deposit) + ) + ); + } + } + // Compute the borrow/withdraw rate, which is the rate at which + // (USD) value is deducted from the account. + Fraction memory db = _toQuoteValue( + info.dydx, + withdraw.marketId, + _getActionRate(withdraw) + ); + // If the deposit to withdraw ratio is >= the minimum collateralization + // rate, then we will never become insolvent at these prices. + if (_gtef(_divf(dd, db), minCr)) { + continue; + } + // The collateralization ratio for this account, parameterized by + // `t` (maker amount), is given by: + // `cr = (supplyValue + t * dd) / (borrowValue + t * db)` + // Solving for `t` gives us: + // `t = (supplyValue - cr * borrowValue) / (cr * db - dd)` + // TODO(dorothy-zbornak): It'll also revert when getting extremely + // close to the minimum collateralization ratio. + Fraction memory t = _divf( + _subf( + Fraction(supplyValue, DYDX_UNITS_BASE), + _mulf(minCr, Fraction(borrowValue, DYDX_UNITS_BASE)) + ), + _subf(_mulf(minCr, db), dd) + ); + solventMakerAmount = LibSafeMath.min256( + solventMakerAmount, + t.n.safeDiv(t.d) + ); + } + } + + /// @dev Create a `BalanceCheckInfo` struct. + /// @param order An order with a `DydxBridge` maker asset. + /// @param dydx The address of the Dydx contract. + /// @return info The `BalanceCheckInfo` struct. + function _getBalanceCheckInfo(LibOrder.Order memory order, address dydx) + private + pure + returns (BalanceCheckInfo memory info) + { + bytes memory rawBridgeData; + (, info.makerTokenAddress, info.bridgeAddress, rawBridgeData) = + LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); + info.dydx = IDydx(dydx); + info.makerAddress = order.makerAddress; + if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) { + (, info.takerTokenAddress) = + LibAssetData.decodeERC20AssetData(order.takerAssetData); + } + info.makerAssetAmount = order.makerAssetAmount; + info.takerAssetAmount = order.takerAssetAmount; + (IDydxBridge.BridgeData memory bridgeData) = + abi.decode(rawBridgeData, (IDydxBridge.BridgeData)); + info.accounts = bridgeData.accountNumbers; + info.actions = bridgeData.actions; + } + + /// @dev Returns the conversion rate for an action, treating infinites as 1. + /// @param action A `BridgeAction`. + function _getActionRate(IDydxBridge.BridgeAction memory action) + private + pure + returns (Fraction memory rate) + { + rate = action.conversionRateDenominator == 0 + ? Fraction(1, 1) + : _normalizef( + Fraction( + action.conversionRateNumerator, + action.conversionRateDenominator + ) + ); + } + + /// @dev Get the global minimum collateralization ratio required for + /// an account to be considered solvent. + /// @param dydx The Dydx interface. + function _getMinimumCollateralizationRatio(IDydx dydx) + private + view + returns (Fraction memory ratio) + { + IDydx.RiskParams memory riskParams = dydx.getRiskParams(); + return _normalizef( + Fraction( + riskParams.marginRatio.value, + DYDX_UNITS_BASE + ) + ); + } + + /// @dev Get the quote (USD) value of a rate within a market. + /// @param dydx The Dydx interface. + /// @param marketId Dydx market ID. + /// @param rate Rate to scale by price. + function _toQuoteValue(IDydx dydx, uint256 marketId, Fraction memory rate) + private + view + returns (Fraction memory quotedRate) + { + IDydx.Price memory price = dydx.getMarketPrice(marketId); + uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId)); + return _mulf( + Fraction( + price.value, + 10 ** uint256(DYDX_UNITS_DECIMALS + tokenDecimals) + ), + rate + ); + } + + /// @dev Get the total supply and borrow values for an account across all markets. + /// @param info State from `_getBalanceCheckInfo()`. + /// @param account The Dydx account identifier. + function _getAccountValues(BalanceCheckInfo memory info, uint256 account) + private + view + returns (uint256 supplyValue, uint256 borrowValue) + { + (IDydx.Value memory supplyValue_, IDydx.Value memory borrowValue_) = + info.dydx.getAdjustedAccountValues(IDydx.AccountInfo( + info.makerAddress, + account + )); + return (supplyValue_.value, borrowValue_.value); + } + + /// @dev Get the amount of an ERC20 token held by `owner` that can be transferred + /// by `spender`. + /// @param tokenAddress The address of the ERC20 token. + /// @param owner The address of the token holder. + /// @param spender The address of the token spender. + function _getTransferabeTokenAmount( + address tokenAddress, + address owner, + address spender + ) + private + view + returns (uint256 transferableAmount) + { + return LibSafeMath.min256( + LibERC20Token.allowance(tokenAddress, owner, spender), + LibERC20Token.balanceOf(tokenAddress, owner) + ); + } + + /*** Fraction helpers ***/ + + /// @dev Check if `a < b`. + function _ltf(Fraction memory a, Fraction memory b) + private + pure + returns (bool isLessThan) + { + return LibFractions.cmp(a.n, a.d, b.n, b.d) == -1; + } + + /// @dev Check if `a > b`. + function _gtf(Fraction memory a, Fraction memory b) + private + pure + returns (bool isGreaterThan) + { + return LibFractions.cmp(a.n, a.d, b.n, b.d) == 1; + } + + /// @dev Check if `a >= b`. + function _gtef(Fraction memory a, Fraction memory b) + private + pure + returns (bool isGreaterThanOrEqual) + { + return !_ltf(a, b); + } + + /// @dev Compute `a + b`. + function _addf(Fraction memory a, Fraction memory b) + private + pure + returns (Fraction memory r) + { + (r.n, r.d) = LibFractions.add(a.n, a.d, b.n, b.d); + } + + /// @dev Compute `a - b`. + function _subf(Fraction memory a, Fraction memory b) + private + pure + returns (Fraction memory r) + { + (r.n, r.d) = LibFractions.sub(a.n, a.d, b.n, b.d); + } + + /// @dev Compute `a * b`. + function _mulf(Fraction memory a, Fraction memory b) + private + pure + returns (Fraction memory r) + { + (r.n, r.d) = LibFractions.mul(a.n, a.d, b.n, b.d); + } + + /// @dev Compute `a / b`. + function _divf(Fraction memory a, Fraction memory b) + private + pure + returns (Fraction memory r) + { + (r.n, r.d) = LibFractions.mul(a.n, a.d, b.d, b.n); + } + + /// @dev Normalize a fraction to prevent arithmetic overflows. + function _normalizef(Fraction memory f) + private + pure + returns (Fraction memory r) + { + (r.n, r.d) = LibFractions.normalize(f.n, f.d); + } +} diff --git a/contracts/dev-utils/contracts/test/TestDydx.sol b/contracts/dev-utils/contracts/test/TestDydx.sol new file mode 100644 index 0000000000..c3c7a62e1c --- /dev/null +++ b/contracts/dev-utils/contracts/test/TestDydx.sol @@ -0,0 +1,162 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; + + +// solhint-disable separate-by-one-line-in-contract +contract TestDydx { + + struct OperatorConfig { + address owner; + address operator; + } + + struct AccountConfig { + address owner; + uint256 accountId; + int256[] balances; + } + + struct MarketInfo { + address token; + uint256 price; + } + + struct TestConfig { + uint256 marginRatio; + OperatorConfig[] operators; + AccountConfig[] accounts; + MarketInfo[] markets; + } + + mapping (bytes32 => bool) private _operators; + mapping (bytes32 => int256) private _balance; + MarketInfo[] private _markets; + uint256 private _marginRatio; + + constructor(TestConfig memory config) public { + _marginRatio = config.marginRatio; + for (uint256 marketId = 0; marketId < config.markets.length; ++marketId) { + _markets.push(config.markets[marketId]); + } + for (uint256 i = 0; i < config.operators.length; ++i) { + OperatorConfig memory op = config.operators[i]; + _operators[_getOperatorHash(op.owner, op.operator)] = true; + } + for (uint256 i = 0; i < config.accounts.length; ++i) { + AccountConfig memory acct = config.accounts[i]; + for (uint256 marketId = 0; marketId < acct.balances.length; ++marketId) { + _balance[_getBalanceHash(acct.owner, acct.accountId, marketId)] = + acct.balances[marketId]; + } + } + } + + function getIsLocalOperator( + address owner, + address operator + ) + external + view + returns (bool isLocalOperator) + { + return _operators[_getOperatorHash(owner, operator)]; + } + + function getMarketTokenAddress( + uint256 marketId + ) + external + view + returns (address tokenAddress) + { + return _markets[marketId].token; + } + + function getRiskParams() + external + view + returns (IDydx.RiskParams memory riskParams) + { + return IDydx.RiskParams({ + marginRatio: IDydx.D256(_marginRatio), + liquidationSpread: IDydx.D256(0), + earningsRate: IDydx.D256(0), + minBorrowedValue: IDydx.Value(0) + }); + } + + function getMarketPrice( + uint256 marketId + ) + external + view + returns (IDydx.Price memory price) + { + return IDydx.Price(_markets[marketId].price); + } + + function getAdjustedAccountValues( + IDydx.AccountInfo calldata account + ) + external + view + returns (IDydx.Value memory supplyValue, IDydx.Value memory borrowValue) + { + for (uint256 marketId = 0; marketId < _markets.length; ++marketId) { + MarketInfo memory market = _markets[marketId]; + int256 balance = + _balance[_getBalanceHash(account.owner, account.number, marketId)]; + uint256 decimals = LibERC20Token.decimals(market.token); + balance = balance * int256(market.price) / int256(10 ** decimals); + if (balance >= 0) { + supplyValue.value += uint256(balance); + } else { + borrowValue.value += uint256(-balance); + } + } + } + + function _getOperatorHash(address owner, address operator) + private + pure + returns (bytes32 operatorHash) + { + return keccak256(abi.encode( + owner, + operator + )); + } + + function _getBalanceHash(address owner, uint256 accountId, uint256 marketId) + private + pure + returns (bytes32 balanceHash) + { + return keccak256(abi.encode( + owner, + accountId, + marketId + )); + } +} diff --git a/contracts/dev-utils/contracts/test/TestLibDydxBalance.sol b/contracts/dev-utils/contracts/test/TestLibDydxBalance.sol new file mode 100644 index 0000000000..0ab8dc037d --- /dev/null +++ b/contracts/dev-utils/contracts/test/TestLibDydxBalance.sol @@ -0,0 +1,116 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; +pragma experimental ABIEncoderV2; + +import "../src/LibDydxBalance.sol"; + + +contract TestLibDydxBalanceToken { + + uint8 public decimals; + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + + constructor(uint8 decimals_) public { + decimals = decimals_; + } + + function setBalance(address owner, uint256 balance) external { + balanceOf[owner] = balance; + } + + function setApproval( + address owner, + address spender, + uint256 allowance_ + ) + external + { + allowance[owner][spender] = allowance_; + } +} + + +contract TestLibDydxBalance { + + mapping (address => TestLibDydxBalanceToken) private tokens; + + function createToken(uint8 decimals) external returns (address) { + TestLibDydxBalanceToken token = new TestLibDydxBalanceToken(decimals); + return address(tokens[address(token)] = token); + } + + function setTokenBalance( + address tokenAddress, + address owner, + uint256 balance + ) + external + { + tokens[tokenAddress].setBalance(owner, balance); + } + + function setTokenApproval( + address tokenAddress, + address owner, + address spender, + uint256 allowance + ) + external + { + tokens[tokenAddress].setApproval(owner, spender, allowance); + } + + function getDydxMakerBalance(LibOrder.Order memory order, address dydx) + public + view + returns (uint256 balance) + { + return LibDydxBalance.getDydxMakerBalance(order, dydx); + } + + function getSolventMakerAmount( + LibDydxBalance.BalanceCheckInfo memory info + ) + public + view + returns (uint256 solventMakerAmount) + { + return LibDydxBalance._getSolventMakerAmount(info); + } + + function getDepositableMakerAmount( + LibDydxBalance.BalanceCheckInfo memory info + ) + public + view + returns (uint256 depositableMakerAmount) + { + return LibDydxBalance._getDepositableMakerAmount(info); + } + + function areActionsWellFormed(LibDydxBalance.BalanceCheckInfo memory info) + public + view + returns (bool areWellFormed) + { + return LibDydxBalance._areActionsWellFormed(info); + } +} diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index 830a721039..b3cf58b3ee 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -8,7 +8,7 @@ "main": "lib/src/index.js", "scripts": { "build": "yarn pre_build && tsc -b", - "test": "yarn assert_deployable", + "test": "yarn assert_deployable && yarn mocha -t 10000 -b ./lib/test/**_test.js", "assert_deployable": "node -e \"const bytecodeLen = (require('./generated-artifacts/DevUtils.json').compilerOutput.evm.bytecode.object.length-2)/2; assert(bytecodeLen<=0x6000,'DevUtils contract is too big to deploy, per EIP-170. '+bytecodeLen+'>'+0x6000)\"", "build:ci": "yarn build", "pre_build": "run-s compile quantify_bytecode contracts:gen generate_contract_wrappers contracts:copy", @@ -27,8 +27,8 @@ "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { - "publicInterfaceContracts": "DevUtils,LibAssetData,LibOrderTransferSimulation,LibTransactionDecoder", - "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).json", + "publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder", + "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/src/artifacts.ts b/contracts/dev-utils/src/artifacts.ts index 37178cef1e..bdc1925bbc 100644 --- a/contracts/dev-utils/src/artifacts.ts +++ b/contracts/dev-utils/src/artifacts.ts @@ -7,11 +7,13 @@ import { ContractArtifact } from 'ethereum-types'; import * as DevUtils from '../generated-artifacts/DevUtils.json'; import * as LibAssetData from '../generated-artifacts/LibAssetData.json'; +import * as LibDydxBalance from '../generated-artifacts/LibDydxBalance.json'; import * as LibOrderTransferSimulation from '../generated-artifacts/LibOrderTransferSimulation.json'; import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; export const artifacts = { DevUtils: DevUtils as ContractArtifact, LibAssetData: LibAssetData as ContractArtifact, + LibDydxBalance: LibDydxBalance as ContractArtifact, LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, }; diff --git a/contracts/dev-utils/src/wrappers.ts b/contracts/dev-utils/src/wrappers.ts index c00882a21a..e3af16194b 100644 --- a/contracts/dev-utils/src/wrappers.ts +++ b/contracts/dev-utils/src/wrappers.ts @@ -5,5 +5,6 @@ */ export * from '../generated-wrappers/dev_utils'; export * from '../generated-wrappers/lib_asset_data'; +export * from '../generated-wrappers/lib_dydx_balance'; export * from '../generated-wrappers/lib_order_transfer_simulation'; export * from '../generated-wrappers/lib_transaction_decoder'; diff --git a/contracts/dev-utils/test/artifacts.ts b/contracts/dev-utils/test/artifacts.ts index 7a57acf66b..e4c1802bc0 100644 --- a/contracts/dev-utils/test/artifacts.ts +++ b/contracts/dev-utils/test/artifacts.ts @@ -11,10 +11,13 @@ import * as DevUtils from '../test/generated-artifacts/DevUtils.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json'; import * as LibAssetData from '../test/generated-artifacts/LibAssetData.json'; +import * as LibDydxBalance from '../test/generated-artifacts/LibDydxBalance.json'; import * as LibOrderTransferSimulation from '../test/generated-artifacts/LibOrderTransferSimulation.json'; import * as LibTransactionDecoder from '../test/generated-artifacts/LibTransactionDecoder.json'; import * as OrderTransferSimulationUtils from '../test/generated-artifacts/OrderTransferSimulationUtils.json'; import * as OrderValidationUtils from '../test/generated-artifacts/OrderValidationUtils.json'; +import * as TestDydx from '../test/generated-artifacts/TestDydx.json'; +import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBalance.json'; export const artifacts = { Addresses: Addresses as ContractArtifact, AssetBalance: AssetBalance as ContractArtifact, @@ -22,8 +25,11 @@ export const artifacts = { EthBalanceChecker: EthBalanceChecker as ContractArtifact, ExternalFunctions: ExternalFunctions as ContractArtifact, LibAssetData: LibAssetData as ContractArtifact, + LibDydxBalance: LibDydxBalance as ContractArtifact, LibOrderTransferSimulation: LibOrderTransferSimulation as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact, OrderValidationUtils: OrderValidationUtils as ContractArtifact, + TestDydx: TestDydx as ContractArtifact, + TestLibDydxBalance: TestLibDydxBalance as ContractArtifact, }; diff --git a/contracts/dev-utils/test/lib_dydx_balance_test.ts b/contracts/dev-utils/test/lib_dydx_balance_test.ts new file mode 100644 index 0000000000..04438f8bc7 --- /dev/null +++ b/contracts/dev-utils/test/lib_dydx_balance_test.ts @@ -0,0 +1,1166 @@ +import { + DydxBridgeAction, + DydxBridgeActionType, + DydxBridgeData, + dydxBridgeDataEncoder, + IAssetDataContract, +} from '@0x/contracts-asset-proxy'; +import { + blockchainTests, + constants, + expect, + getRandomFloat, + getRandomInteger, + Numberish, + randomAddress, +} from '@0x/contracts-test-utils'; +import { Order } from '@0x/types'; +import { BigNumber, fromTokenUnitAmount, toTokenUnitAmount } from '@0x/utils'; + +import { artifacts as devUtilsArtifacts } from './artifacts'; +import { TestDydxContract, TestLibDydxBalanceContract } from './wrappers'; + +blockchainTests('LibDydxBalance', env => { + interface TestDydxConfig { + marginRatio: BigNumber; + operators: Array<{ + owner: string; + operator: string; + }>; + accounts: Array<{ + owner: string; + accountId: BigNumber; + balances: BigNumber[]; + }>; + markets: Array<{ + token: string; + decimals: number; + price: BigNumber; + }>; + } + + const MARGIN_RATIO = 1.5; + const PRICE_DECIMALS = 18; + const MAKER_DECIMALS = 6; + const TAKER_DECIMALS = 18; + const INITIAL_TAKER_TOKEN_BALANCE = fromTokenUnitAmount(1000, TAKER_DECIMALS); + const BRIDGE_ADDRESS = randomAddress(); + const ACCOUNT_OWNER = randomAddress(); + const MAKER_PRICE = 150; + const TAKER_PRICE = 100; + const SOLVENT_ACCOUNT_IDX = 0; + // const MIN_SOLVENT_ACCOUNT_IDX = 1; + const INSOLVENT_ACCOUNT_IDX = 2; + const ZERO_BALANCE_ACCOUNT_IDX = 3; + const DYDX_CONFIG: TestDydxConfig = { + marginRatio: fromTokenUnitAmount(MARGIN_RATIO, PRICE_DECIMALS), + operators: [{ owner: ACCOUNT_OWNER, operator: BRIDGE_ADDRESS }], + accounts: [ + { + owner: ACCOUNT_OWNER, + accountId: getRandomInteger(1, 2 ** 64), + // Account exceeds collateralization. + balances: [fromTokenUnitAmount(10, TAKER_DECIMALS), fromTokenUnitAmount(-1, MAKER_DECIMALS)], + }, + { + owner: ACCOUNT_OWNER, + accountId: getRandomInteger(1, 2 ** 64), + // Account is at minimum collateralization. + balances: [ + fromTokenUnitAmount((MAKER_PRICE / TAKER_PRICE) * MARGIN_RATIO * 5, TAKER_DECIMALS), + fromTokenUnitAmount(-5, MAKER_DECIMALS), + ], + }, + { + owner: ACCOUNT_OWNER, + accountId: getRandomInteger(1, 2 ** 64), + // Account is undercollateralized.. + balances: [fromTokenUnitAmount(1, TAKER_DECIMALS), fromTokenUnitAmount(-2, MAKER_DECIMALS)], + }, + { + owner: ACCOUNT_OWNER, + accountId: getRandomInteger(1, 2 ** 64), + // Account has no balance. + balances: [fromTokenUnitAmount(0, TAKER_DECIMALS), fromTokenUnitAmount(0, MAKER_DECIMALS)], + }, + ], + markets: [ + { + token: constants.NULL_ADDRESS, // TBD + decimals: TAKER_DECIMALS, + price: fromTokenUnitAmount(TAKER_PRICE, PRICE_DECIMALS), + }, + { + token: constants.NULL_ADDRESS, // TBD + decimals: MAKER_DECIMALS, + price: fromTokenUnitAmount(MAKER_PRICE, PRICE_DECIMALS), + }, + ], + }; + + let dydx: TestDydxContract; + let testContract: TestLibDydxBalanceContract; + let assetDataContract: IAssetDataContract; + let takerTokenAddress: string; + let makerTokenAddress: string; + + before(async () => { + assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, env.provider); + + testContract = await TestLibDydxBalanceContract.deployWithLibrariesFrom0xArtifactAsync( + devUtilsArtifacts.TestLibDydxBalance, + devUtilsArtifacts, + env.provider, + env.txDefaults, + {}, + ); + + // Create tokens. + takerTokenAddress = await testContract.createToken(TAKER_DECIMALS).callAsync(); + await testContract.createToken(TAKER_DECIMALS).awaitTransactionSuccessAsync(); + makerTokenAddress = await testContract.createToken(MAKER_DECIMALS).callAsync(); + await testContract.createToken(MAKER_DECIMALS).awaitTransactionSuccessAsync(); + + DYDX_CONFIG.markets[0].token = takerTokenAddress; + DYDX_CONFIG.markets[1].token = makerTokenAddress; + + dydx = await TestDydxContract.deployFrom0xArtifactAsync( + devUtilsArtifacts.TestDydx, + env.provider, + env.txDefaults, + {}, + DYDX_CONFIG, + ); + + // Mint taker tokens. + await testContract + .setTokenBalance(takerTokenAddress, ACCOUNT_OWNER, INITIAL_TAKER_TOKEN_BALANCE) + .awaitTransactionSuccessAsync(); + // Approve the Dydx contract to spend takerToken. + await testContract + .setTokenApproval(takerTokenAddress, ACCOUNT_OWNER, dydx.address, constants.MAX_UINT256) + .awaitTransactionSuccessAsync(); + }); + + interface BalanceCheckInfo { + dydx: string; + bridgeAddress: string; + makerAddress: string; + makerTokenAddress: string; + takerTokenAddress: string; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + accounts: BigNumber[]; + actions: DydxBridgeAction[]; + } + + function createBalanceCheckInfo(fields: Partial = {}): BalanceCheckInfo { + return { + dydx: dydx.address, + bridgeAddress: BRIDGE_ADDRESS, + makerAddress: ACCOUNT_OWNER, + makerTokenAddress: DYDX_CONFIG.markets[1].token, + takerTokenAddress: DYDX_CONFIG.markets[0].token, + makerAssetAmount: fromTokenUnitAmount(10, MAKER_DECIMALS), + takerAssetAmount: fromTokenUnitAmount(5, TAKER_DECIMALS), + accounts: [DYDX_CONFIG.accounts[SOLVENT_ACCOUNT_IDX].accountId], + actions: [], + ...fields, + }; + } + + function getFilledAccountCollateralizations( + config: TestDydxConfig, + checkInfo: BalanceCheckInfo, + makerAssetFillAmount: BigNumber, + ): BigNumber[] { + const values: BigNumber[][] = checkInfo.accounts.map((accountId, accountIdx) => { + const accountBalances = config.accounts[accountIdx].balances.slice(); + for (const action of checkInfo.actions) { + const actionMarketId = action.marketId.toNumber(); + const actionAccountIdx = action.accountIdx.toNumber(); + if (checkInfo.accounts[actionAccountIdx] !== accountId) { + continue; + } + const rate = action.conversionRateDenominator.eq(0) + ? new BigNumber(1) + : action.conversionRateNumerator.div(action.conversionRateDenominator); + const change = makerAssetFillAmount.times( + action.actionType === DydxBridgeActionType.Deposit ? rate : rate.negated(), + ); + accountBalances[actionMarketId] = change.plus(accountBalances[actionMarketId]); + } + return accountBalances.map((b, marketId) => + toTokenUnitAmount(b, config.markets[marketId].decimals).times( + toTokenUnitAmount(config.markets[marketId].price, PRICE_DECIMALS), + ), + ); + }); + return values + .map(accountValues => { + return [ + // supply + BigNumber.sum(...accountValues.filter(b => b.gte(0))), + // borrow + BigNumber.sum(...accountValues.filter(b => b.lt(0))).abs(), + ]; + }) + .map(([supply, borrow]) => supply.div(borrow)); + } + + function getRandomRate(): BigNumber { + return getRandomFloat(0, 1); + } + + // Computes a deposit rate that is the minimum to keep an account solvent + // perpetually. + function getBalancedDepositRate(withdrawRate: BigNumber, scaling: Numberish = 1.000001): BigNumber { + return withdrawRate.times((MAKER_PRICE / TAKER_PRICE) * MARGIN_RATIO).times(scaling); + } + + function takerToMakerAmount(takerAmount: BigNumber): BigNumber { + return takerAmount.times(new BigNumber(10).pow(MAKER_DECIMALS - TAKER_DECIMALS)); + } + + describe('_getSolventMakerAmount()', () => { + it('computes fillable amount for a solvent maker', async () => { + // Deposit collateral at a rate low enough to steadily reduce the + // withdraw account's collateralization ratio. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate, Math.random()); + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // The collateralization ratio after filling `makerAssetFillAmount` + // should be exactly at `MARGIN_RATIO`. + const cr = getFilledAccountCollateralizations(DYDX_CONFIG, checkInfo, makerAssetFillAmount); + expect(cr[0].dp(2)).to.bignumber.eq(MARGIN_RATIO); + }); + + it('computes fillable amount for a solvent maker with zero-sized deposits', async () => { + const withdrawRate = getRandomRate(); + const depositRate = new BigNumber(0); + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // The collateralization ratio after filling `makerAssetFillAmount` + // should be exactly at `MARGIN_RATIO`. + const cr = getFilledAccountCollateralizations(DYDX_CONFIG, checkInfo, makerAssetFillAmount); + expect(cr[0].dp(2)).to.bignumber.eq(MARGIN_RATIO); + }); + + it('computes fillable amount for a solvent maker with no deposits', async () => { + const withdrawRate = getRandomRate(); + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // The collateralization ratio after filling `makerAssetFillAmount` + // should be exactly at `MARGIN_RATIO`. + const cr = getFilledAccountCollateralizations(DYDX_CONFIG, checkInfo, makerAssetFillAmount); + expect(cr[0].dp(2)).to.bignumber.eq(MARGIN_RATIO); + }); + + it('computes fillable amount for a solvent maker with multiple deposits', async () => { + // Deposit collateral at a rate low enough to steadily reduce the + // withdraw account's collateralization ratio. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate, Math.random()); + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.75), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.25), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // The collateralization ratio after filling `makerAssetFillAmount` + // should be exactly at `MARGIN_RATIO`. + const cr = getFilledAccountCollateralizations(DYDX_CONFIG, checkInfo, makerAssetFillAmount); + expect(cr[0].dp(2)).to.bignumber.eq(MARGIN_RATIO); + }); + + it('returns infinite amount for a perpetually solvent maker', async () => { + // Deposit collateral at a rate that keeps the withdraw account's + // collateralization ratio constant. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate); + const checkInfo = createBalanceCheckInfo({ + // Deposit/Withdraw at a rate == marginRatio. + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }); + + it('returns infinite amount for a perpetually solvent maker with multiple deposits', async () => { + // Deposit collateral at a rate that keeps the withdraw account's + // collateralization ratio constant. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate); + const checkInfo = createBalanceCheckInfo({ + // Deposit/Withdraw at a rate == marginRatio. + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.25), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.75), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }); + + it('does not count deposits to other accounts', async () => { + // Deposit collateral at a rate that keeps the withdraw account's + // collateralization ratio constant, BUT we split it in two deposits + // and one will go into a different account. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate); + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.5), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Deposit, + // Deposit enough to balance out withdraw, but + // into a different account. + accountIdx: new BigNumber(1), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate.times(0.5), TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + }); + + it('returns zero on an account that is under-collateralized', async () => { + // Even though the deposit rate is enough to meet the minimum collateralization ratio, + // the account is under-collateralized from the start, so cannot be filled. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate); + const checkInfo = createBalanceCheckInfo({ + accounts: [DYDX_CONFIG.accounts[INSOLVENT_ACCOUNT_IDX].accountId], + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(0); + }); + + it( + 'returns zero on an account that has no balance if deposit ' + + 'to withdraw ratio is < the minimum collateralization rate', + async () => { + // If the deposit rate is not enough to meet the minimum collateralization ratio, + // the fillable maker amount is zero because it will become insolvent as soon as + // the withdraw occurs. + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate, 0.99); + const checkInfo = createBalanceCheckInfo({ + accounts: [DYDX_CONFIG.accounts[ZERO_BALANCE_ACCOUNT_IDX].accountId], + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(0); + }, + ); + + it( + 'returns infinite on an account that has no balance if deposit ' + + 'to withdraw ratio is >= the minimum collateralization rate', + async () => { + const withdrawRate = getRandomRate(); + const depositRate = getBalancedDepositRate(withdrawRate); + const checkInfo = createBalanceCheckInfo({ + accounts: [DYDX_CONFIG.accounts[ZERO_BALANCE_ACCOUNT_IDX].accountId], + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: fromTokenUnitAmount(withdrawRate), + conversionRateDenominator: fromTokenUnitAmount(1), + }, + ], + }); + const makerAssetFillAmount = await testContract.getSolventMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }, + ); + }); + + blockchainTests.resets('_getDepositableMakerAmount()', () => { + it('returns infinite if no deposit action', async () => { + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + actions: [], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }); + + it('returns infinite if deposit rate is zero', async () => { + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(0, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }); + + it('returns infinite if taker tokens cover the deposit rate', async () => { + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(Math.random() * 0.1, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(constants.MAX_UINT256); + }); + + it('returns correct amount if taker tokens only partially cover deposit rate', async () => { + // The taker tokens getting exchanged in will only partially cover the deposit. + const exchangeRate = 0.1; + const depositRate = Math.random() + exchangeRate; + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // Compute the equivalent taker asset fill amount. + const takerAssetFillAmount = fromTokenUnitAmount( + toTokenUnitAmount(makerAssetFillAmount, MAKER_DECIMALS) + // Reduce the deposit rate by the exchange rate. + .times(depositRate - exchangeRate), + TAKER_DECIMALS, + ); + // Which should equal the entire taker token balance of the account owner. + // We do some rounding to account for integer vs FP vs symbolic precision differences. + expect(toTokenUnitAmount(takerAssetFillAmount, TAKER_DECIMALS).dp(5)).to.bignumber.eq( + toTokenUnitAmount(INITIAL_TAKER_TOKEN_BALANCE, TAKER_DECIMALS).dp(5), + ); + }); + + it('returns correct amount if the taker asset not an ERC20', async () => { + const depositRate = 0.1; + const checkInfo = createBalanceCheckInfo({ + // The `takerTokenAddress` will be zero if the asset is not an ERC20. + takerTokenAddress: constants.NULL_ADDRESS, + takerAssetAmount: new BigNumber(1), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // Compute the equivalent taker asset fill amount. + const takerAssetFillAmount = fromTokenUnitAmount( + toTokenUnitAmount(makerAssetFillAmount, MAKER_DECIMALS) + // Reduce the deposit rate by the exchange rate. + .times(depositRate), + TAKER_DECIMALS, + ); + // Which should equal the entire taker token balance of the account owner. + // We do some rounding to account for integer vs FP vs symbolic precision differences. + expect(toTokenUnitAmount(takerAssetFillAmount, TAKER_DECIMALS).dp(6)).to.bignumber.eq( + toTokenUnitAmount(INITIAL_TAKER_TOKEN_BALANCE, TAKER_DECIMALS).dp(6), + ); + }); + + it('returns the correct amount if taker:maker deposit rate is 1:1 and' + 'token != taker token', async () => { + const checkInfo = createBalanceCheckInfo({ + takerTokenAddress: randomAddress(), + // These amounts should be effectively ignored in the final computation + // because the token being deposited is not the taker token. + takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(1, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(takerToMakerAmount(INITIAL_TAKER_TOKEN_BALANCE)); + }); + + it('returns the smallest viable maker amount with multiple deposits', async () => { + // The taker tokens getting exchanged in will only partially cover the deposit. + const exchangeRate = 0.1; + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + actions: [ + // Technically, deposits of the same token are not allowed, but the + // check isn't done in this function so we'll do this to simulate + // two deposits to distinct tokens. + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(Math.random() + exchangeRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(Math.random() + exchangeRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.not.bignumber.eq(constants.MAX_UINT256); + // Extract the deposit rates. + const depositRates = checkInfo.actions.map(a => + toTokenUnitAmount(a.conversionRateNumerator, TAKER_DECIMALS).div( + toTokenUnitAmount(a.conversionRateDenominator, MAKER_DECIMALS), + ), + ); + // The largest deposit rate will result in the smallest maker asset fill amount. + const maxDepositRate = BigNumber.max(...depositRates); + // Compute the equivalent taker asset fill amounts. + const takerAssetFillAmount = fromTokenUnitAmount( + toTokenUnitAmount(makerAssetFillAmount, MAKER_DECIMALS) + // Reduce the deposit rate by the exchange rate. + .times(maxDepositRate.minus(exchangeRate)), + TAKER_DECIMALS, + ); + // Which should equal the entire taker token balance of the account owner. + // We do some rounding to account for integer vs FP vs symbolic precision differences. + expect(toTokenUnitAmount(takerAssetFillAmount, TAKER_DECIMALS).dp(5)).to.bignumber.eq( + toTokenUnitAmount(INITIAL_TAKER_TOKEN_BALANCE, TAKER_DECIMALS).dp(5), + ); + }); + + it( + 'returns zero if the maker has no taker tokens and the deposit rate is' + 'greater than the exchange rate', + async () => { + await testContract + .setTokenBalance(takerTokenAddress, ACCOUNT_OWNER, constants.ZERO_AMOUNT) + .awaitTransactionSuccessAsync(); + // The taker tokens getting exchanged in will only partially cover the deposit. + const exchangeRate = 0.1; + const depositRate = Math.random() + exchangeRate; + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(0); + }, + ); + + it( + 'returns zero if dydx has no taker token allowance and the deposit rate is' + + 'greater than the exchange rate', + async () => { + await testContract + .setTokenApproval(takerTokenAddress, ACCOUNT_OWNER, dydx.address, constants.ZERO_AMOUNT) + .awaitTransactionSuccessAsync(); + // The taker tokens getting exchanged in will only partially cover the deposit. + const exchangeRate = 0.1; + const depositRate = Math.random() + exchangeRate; + const checkInfo = createBalanceCheckInfo({ + takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(depositRate, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + ], + }); + const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); + expect(makerAssetFillAmount).to.bignumber.eq(0); + }, + ); + }); + + describe('_areActionsWellFormed()', () => { + it('Returns false if no actions', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if there is an account index out of range in deposits', async () => { + const checkInfo = createBalanceCheckInfo({ + accounts: DYDX_CONFIG.accounts.slice(0, 2).map(a => a.accountId), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(2), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if a market is not unique among deposits', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if no withdraw at the end', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if a withdraw comes before a deposit', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if more than one withdraw', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if withdraw is not for maker token', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Returns false if withdraw is for an out of range account', async () => { + const checkInfo = createBalanceCheckInfo({ + accounts: DYDX_CONFIG.accounts.slice(0, 2).map(a => a.accountId), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(2), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.false(); + }); + + it('Can return true if no deposit', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.true(); + }); + + it('Can return true if no deposit', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.true(); + }); + + it('Can return true with multiple deposits', async () => { + const checkInfo = createBalanceCheckInfo({ + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }); + const r = await testContract.areActionsWellFormed(checkInfo).callAsync(); + expect(r).to.be.true(); + }); + }); + + function createERC20AssetData(tokenAddress: string): string { + return assetDataContract.ERC20Token(tokenAddress).getABIEncodedTransactionData(); + } + + function createERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { + return assetDataContract.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData(); + } + + function createBridgeAssetData( + makerTokenAddress_: string, + bridgeAddress: string, + data: Partial = {}, + ): string { + return assetDataContract + .ERC20Bridge( + makerTokenAddress_, + bridgeAddress, + dydxBridgeDataEncoder.encode({ + bridgeData: { + accountNumbers: DYDX_CONFIG.accounts.slice(0, 1).map(a => a.accountId), + actions: [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: fromTokenUnitAmount(1, TAKER_DECIMALS), + conversionRateDenominator: fromTokenUnitAmount(1, MAKER_DECIMALS), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + ...data, + }, + }), + ) + .getABIEncodedTransactionData(); + } + + function createOrder(orderFields: Partial = {}): Order { + return { + chainId: 1, + exchangeAddress: randomAddress(), + salt: getRandomInteger(1, constants.MAX_UINT256), + expirationTimeSeconds: getRandomInteger(1, constants.MAX_UINT256), + feeRecipientAddress: randomAddress(), + makerAddress: ACCOUNT_OWNER, + takerAddress: constants.NULL_ADDRESS, + senderAddress: constants.NULL_ADDRESS, + makerFee: getRandomInteger(1, constants.MAX_UINT256), + takerFee: getRandomInteger(1, constants.MAX_UINT256), + makerAssetAmount: fromTokenUnitAmount(100, TAKER_DECIMALS), + takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), + makerAssetData: createBridgeAssetData(makerTokenAddress, BRIDGE_ADDRESS), + takerAssetData: createERC20AssetData(takerTokenAddress), + makerFeeAssetData: constants.NULL_BYTES, + takerFeeAssetData: constants.NULL_BYTES, + ...orderFields, + }; + } + + describe('getDydxMakerBalance()', () => { + it('returns nonzero with valid order', async () => { + const order = createOrder(); + const r = await testContract.getDydxMakerBalance(order, dydx.address).callAsync(); + expect(r).to.not.bignumber.eq(0); + }); + + it('returns nonzero with valid order with an ERC721 taker asset', async () => { + const order = createOrder({ + takerAssetData: createERC721AssetData(randomAddress(), getRandomInteger(1, constants.MAX_UINT256)), + }); + const r = await testContract.getDydxMakerBalance(order, dydx.address).callAsync(); + expect(r).to.not.bignumber.eq(0); + }); + + it('returns 0 if bridge is not a local operator', async () => { + const order = createOrder({ + makerAssetData: createBridgeAssetData(ACCOUNT_OWNER, randomAddress()), + }); + const r = await testContract.getDydxMakerBalance(order, dydx.address).callAsync(); + expect(r).to.bignumber.eq(0); + }); + + it('returns 0 if bridge data does not have well-formed actions', async () => { + const order = createOrder({ + makerAssetData: createBridgeAssetData(takerTokenAddress, BRIDGE_ADDRESS, { + // Two withdraw actions is invalid. + actions: [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(0), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(0), + conversionRateDenominator: new BigNumber(0), + }, + ], + }), + }); + const r = await testContract.getDydxMakerBalance(order, dydx.address).callAsync(); + expect(r).to.bignumber.eq(0); + }); + + it('returns 0 if the maker token withdraw rate is < 1', async () => { + const order = createOrder({ + makerAssetData: createBridgeAssetData(takerTokenAddress, BRIDGE_ADDRESS, { + actions: [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: new BigNumber(0), + marketId: new BigNumber(1), + conversionRateNumerator: new BigNumber(9e18), + conversionRateDenominator: new BigNumber(10e18), + }, + ], + }), + }); + const r = await testContract.getDydxMakerBalance(order, dydx.address).callAsync(); + expect(r).to.bignumber.eq(0); + }); + }); +}); +// tslint:disable-next-line: max-file-line-count diff --git a/contracts/dev-utils/test/wrappers.ts b/contracts/dev-utils/test/wrappers.ts index 0760a7390d..01f291fa09 100644 --- a/contracts/dev-utils/test/wrappers.ts +++ b/contracts/dev-utils/test/wrappers.ts @@ -9,7 +9,10 @@ export * from '../test/generated-wrappers/dev_utils'; export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/external_functions'; export * from '../test/generated-wrappers/lib_asset_data'; +export * from '../test/generated-wrappers/lib_dydx_balance'; export * from '../test/generated-wrappers/lib_order_transfer_simulation'; export * from '../test/generated-wrappers/lib_transaction_decoder'; export * from '../test/generated-wrappers/order_transfer_simulation_utils'; export * from '../test/generated-wrappers/order_validation_utils'; +export * from '../test/generated-wrappers/test_dydx'; +export * from '../test/generated-wrappers/test_lib_dydx_balance'; diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index 9c2a01383f..eccc9bf633 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -5,6 +5,7 @@ "files": [ "generated-artifacts/DevUtils.json", "generated-artifacts/LibAssetData.json", + "generated-artifacts/LibDydxBalance.json", "generated-artifacts/LibOrderTransferSimulation.json", "generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/Addresses.json", @@ -13,10 +14,13 @@ "test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/ExternalFunctions.json", "test/generated-artifacts/LibAssetData.json", + "test/generated-artifacts/LibDydxBalance.json", "test/generated-artifacts/LibOrderTransferSimulation.json", "test/generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/OrderTransferSimulationUtils.json", - "test/generated-artifacts/OrderValidationUtils.json" + "test/generated-artifacts/OrderValidationUtils.json", + "test/generated-artifacts/TestDydx.json", + "test/generated-artifacts/TestLibDydxBalance.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/integrations/CHANGELOG.json b/contracts/integrations/CHANGELOG.json index cfdc905da5..fd221e8db4 100644 --- a/contracts/integrations/CHANGELOG.json +++ b/contracts/integrations/CHANGELOG.json @@ -31,6 +31,10 @@ { "note": "Update tests for refactored `DevUtils`", "pr": 2464 + }, + { + "note": "Add DydxBridge validation", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts b/contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts index 17b117a8f0..f4992117c7 100644 --- a/contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts +++ b/contracts/integrations/test/bridges/dydx_bridge_mainnet_test.ts @@ -23,14 +23,14 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => { const defaultAmount = toBaseUnitAmount(0.01); const defaultDepositAction = { actionType: DydxBridgeActionType.Deposit as number, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId: daiMarketId, conversionRateNumerator: constants.ZERO_AMOUNT, conversionRateDenominator: constants.ZERO_AMOUNT, }; const defaultWithdrawAction = { actionType: DydxBridgeActionType.Withdraw as number, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId: daiMarketId, // This ratio must be less than the `1` to account // for interest in dydx balances because the test @@ -71,7 +71,7 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => { case DydxBridgeActionType.Deposit: expectedDepositEvents.push({ accountOwner: dydxAccountOwner, - accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()], + accountNumber: bridgeData.accountNumbers[action.accountIdx.toNumber()], market: action.marketId, update: { deltaWei: { sign: true, value: scaledAmount } }, from: dydxAccountOwner, @@ -81,7 +81,7 @@ blockchainTests.fork.resets('Mainnet dydx bridge tests', env => { case DydxBridgeActionType.Withdraw: expectedWithdrawEvents.push({ accountOwner: dydxAccountOwner, - accountNumber: bridgeData.accountNumbers[action.accountId.toNumber()], + accountNumber: bridgeData.accountNumbers[action.accountIdx.toNumber()], market: action.marketId, update: { deltaWei: { sign: false, value: scaledAmount } }, to: receiver, diff --git a/contracts/integrations/test/dev-utils/dev_utils_mainnet_test.ts b/contracts/integrations/test/dev-utils/dev_utils_mainnet_test.ts index 1aa36ff346..db00762350 100644 --- a/contracts/integrations/test/dev-utils/dev_utils_mainnet_test.ts +++ b/contracts/integrations/test/dev-utils/dev_utils_mainnet_test.ts @@ -34,6 +34,7 @@ blockchainTests.fork.resets('DevUtils mainnet tests', env => { devUtilsArtifacts, contractAddresses.exchange, contractAddresses.chaiBridge, + contractAddresses.dydxBridge, ); await dai.approve(chai.address, constants.MAX_UINT256).awaitTransactionSuccessAsync({ from: daiHolder }); await chai.join(daiHolder, daiDepositAmount).awaitTransactionSuccessAsync({ from: daiHolder }); diff --git a/contracts/integrations/test/dev-utils/get_order_hash.ts b/contracts/integrations/test/dev-utils/get_order_hash.ts index 862b9a188b..ca8e53430b 100644 --- a/contracts/integrations/test/dev-utils/get_order_hash.ts +++ b/contracts/integrations/test/dev-utils/get_order_hash.ts @@ -27,6 +27,7 @@ blockchainTests('DevUtils.getOrderHash', env => { artifacts, exchange.address, constants.NULL_ADDRESS, + constants.NULL_ADDRESS, ); }); diff --git a/contracts/integrations/test/dev-utils/lib_asset_data.ts b/contracts/integrations/test/dev-utils/lib_asset_data.ts index 7594a44f11..5592c2243d 100644 --- a/contracts/integrations/test/dev-utils/lib_asset_data.ts +++ b/contracts/integrations/test/dev-utils/lib_asset_data.ts @@ -81,6 +81,7 @@ blockchainTests.resets('LibAssetData', env => { artifacts, deployment.exchange.address, constants.NULL_ADDRESS, + constants.NULL_ADDRESS, ); staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync( diff --git a/contracts/integrations/test/dev-utils/lib_transaction_decoder.ts b/contracts/integrations/test/dev-utils/lib_transaction_decoder.ts index 98313cd893..7b3cb76a5b 100644 --- a/contracts/integrations/test/dev-utils/lib_transaction_decoder.ts +++ b/contracts/integrations/test/dev-utils/lib_transaction_decoder.ts @@ -43,6 +43,7 @@ blockchainTests('LibTransactionDecoder', env => { artifacts, exchange.address, constants.NULL_ADDRESS, + constants.NULL_ADDRESS, ); }); diff --git a/contracts/integrations/test/exchange/fill_dydx_order_test.ts b/contracts/integrations/test/exchange/fill_dydx_order_test.ts index ba99d7fe4c..5b410a906f 100644 --- a/contracts/integrations/test/exchange/fill_dydx_order_test.ts +++ b/contracts/integrations/test/exchange/fill_dydx_order_test.ts @@ -34,14 +34,14 @@ blockchainTests.resets('Exchange fills dydx orders', env => { let testTokenAddress: string; const defaultDepositAction = { actionType: DydxBridgeActionType.Deposit as number, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId, conversionRateNumerator: dydxConversionRateNumerator, conversionRateDenominator: dydxConversionRateDenominator, }; const defaultWithdrawAction = { actionType: DydxBridgeActionType.Withdraw as number, - accountId: constants.ZERO_AMOUNT, + accountIdx: constants.ZERO_AMOUNT, marketId, conversionRateNumerator: constants.ZERO_AMOUNT, conversionRateDenominator: constants.ZERO_AMOUNT, diff --git a/contracts/integrations/test/framework/deployment_manager.ts b/contracts/integrations/test/framework/deployment_manager.ts index 4b0dd9aa8b..fae0ffc519 100644 --- a/contracts/integrations/test/framework/deployment_manager.ts +++ b/contracts/integrations/test/framework/deployment_manager.ts @@ -203,6 +203,7 @@ export class DeploymentManager { devUtilsArtifacts, exchange.address, constants.NULL_ADDRESS, + constants.NULL_ADDRESS, ); // Construct the new instance and return it. diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index 251907e9c5..3551b3a3d1 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -37,6 +37,10 @@ { "note": "Export `EvmBytecodeOutputLinkReferences` type.", "pr": 2462 + }, + { + "note": "Add more functions to `LibFractions`.", + "pr": 2466 } ], "timestamp": 1580811564 diff --git a/contracts/utils/contracts/src/LibFractions.sol b/contracts/utils/contracts/src/LibFractions.sol index f5a05706c6..1582733988 100644 --- a/contracts/utils/contracts/src/LibFractions.sol +++ b/contracts/utils/contracts/src/LibFractions.sol @@ -37,7 +37,101 @@ library LibFractions { .safeMul(d2) .safeAdd(n2.safeMul(d1)); denominator = d1.safeMul(d2); - return (numerator, denominator); + return normalize(numerator, denominator); + } + + /// @dev Safely subracts two fractions `n1/d1 - n2/d2` + /// @param n1 numerator of `1` + /// @param d1 denominator of `1` + /// @param n2 numerator of `2` + /// @param d2 denominator of `2` + /// @return numerator Numerator of sum + /// @return denominator Denominator of sum + function sub( + uint256 n1, + uint256 d1, + uint256 n2, + uint256 d2 + ) + internal + pure + returns ( + uint256 numerator, + uint256 denominator + ) + { + if (n2 == 0) { + return (numerator = n1, denominator = d1); + } + numerator = n1 + .safeMul(d2) + .safeSub(n2.safeMul(d1)); + denominator = d1.safeMul(d2); + return normalize(numerator, denominator); + } + + /// @dev Multiply two fractions. + /// @param n1 numerator of `1` + /// @param d1 denominator of `1` + /// @param n2 numerator of `2` + /// @param d2 denominator of `2` + /// @return numerator numerator of product. + /// @return denominator numerator of product. + function mul( + uint256 n1, + uint256 d1, + uint256 n2, + uint256 d2 + ) + internal + pure + returns ( + uint256 numerator, + uint256 denominator + ) + { + return normalize(n1.safeMul(n2), d1.safeMul(d2)); + } + + /// @dev Compares two fractions. + /// @param n1 numerator of `1` + /// @param d1 denominator of `1` + /// @param n2 numerator of `2` + /// @param d2 denominator of `2` + /// @return compareResult + /// `-1` if `n1/d1 < n2/d2`. + /// `0` if `n1/d1 == n2/d2`. + /// `1` if `n1/d1 > n2/d2`. + function cmp( + uint256 n1, + uint256 d1, + uint256 n2, + uint256 d2 + ) + internal + pure + returns (int8 compareResult) + { + // Handle infinities. + if (d1 == 0) { + if (d2 == 0) { + return 0; + } + return 1; + } else { + if (d2 == 0) { + return -1; + } + } + uint256 nd1 = n1.safeMul(d2); + uint256 nd2 = n2.safeMul(d1); + if (nd1 > nd2) { + return 1; + } + if (nd1 < nd2) { + return -1; + } + return 0; } /// @dev Rescales a fraction to prevent overflows during addition if either diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 13e746ff6a..03c143c90a 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Support deploying contracts with unliked libraries through `deployWithLibrariesFrom0xArtifactAsync()`", "pr": 2463 + }, + { + "note": "Update reference outputs", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 54f09768de..b65cf98d4f 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -60,7 +60,7 @@ }, { "note": "Update snapshot addresses", - "pr": 2464 + "pr": 2466 } ], "timestamp": 1580811564 diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index bdb4821599..4b50df8450 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -131,18 +131,18 @@ "etherToken": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082", "exchange": "0x48bacb9266a570d521063ef5dd96e61686dbe788", "assetProxyOwner": "0x0000000000000000000000000000000000000000", - "erc20BridgeProxy": "0x038f9b392fb9a9676dbaddf78ea5fdbf6c7d9710", + "erc20BridgeProxy": "0x371b13d97f4bf77d724e78c16b7dc74099f40e84", "zeroExGovernor": "0x0000000000000000000000000000000000000000", "forwarder": "0xe704967449b57b2382b7fa482718748c13c63190", "coordinatorRegistry": "0xaa86dda78e9434aca114b6676fc742a18d15a1cc", "coordinator": "0x4d3d5c850dd5bd9d6f4adda3dd039a3c8054ca29", "multiAssetProxy": "0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db", "staticCallProxy": "0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f", - "devUtils": "0x74341e87b1c4db7d5ed95f92b37509f2525a7a90", + "devUtils": "0xb23672f74749bf7916ba6827c64111a4d6de7f11", "exchangeV2": "0x48bacb9266a570d521063ef5dd96e61686dbe788", - "zrxVault": "0xc4df27466183c0fe2a5924d6ea56e334deff146a", - "staking": "0xf23276778860e420acfc18ebeebf7e829b06965c", - "stakingProxy": "0x8a063452f7df2614db1bca3a85ef35da40cf0835", + "zrxVault": "0xf23276778860e420acfc18ebeebf7e829b06965c", + "staking": "0x8a063452f7df2614db1bca3a85ef35da40cf0835", + "stakingProxy": "0x59adefa01843c627ba5d6aa350292b4b7ccae67a", "uniswapBridge": "0x0000000000000000000000000000000000000000", "eth2DaiBridge": "0x0000000000000000000000000000000000000000", "erc20BridgeSampler": "0x0000000000000000000000000000000000000000", diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index 33a90c586a..7293dc0490 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -18,7 +18,7 @@ "changes": [ { "note": "Update `DevUtils` artifact", - "pr": 2464 + "pr": 2466 }, { "note": "Remove `LibTransactionDecoder` artifact", diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index 9127f14eec..dd8474eeb1 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -23,6 +23,10 @@ { "note": "Use contract package artifacts in ganache migrations", "pr": 2456 + }, + { + "note": "Update deployment for new DevUtils", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index c6655b99d9..6c07f2c7c2 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -198,6 +198,7 @@ export async function runMigrationsAsync( allArtifacts, exchange.address, constants.NULL_ADDRESS, + constants.NULL_ADDRESS, ); // tslint:disable-next-line:no-unused-variable diff --git a/packages/migrations/src/testnet_migrations.ts b/packages/migrations/src/testnet_migrations.ts index f5b6f45324..df9aa2a779 100644 --- a/packages/migrations/src/testnet_migrations.ts +++ b/packages/migrations/src/testnet_migrations.ts @@ -120,7 +120,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t assetProxyArtifacts, ); - await DydxBridgeContract.deployFrom0xArtifactAsync( + const dydxBridge = await DydxBridgeContract.deployFrom0xArtifactAsync( assetProxyArtifacts.DydxBridge, provider, txDefaults, @@ -253,6 +253,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t devUtilsArtifacts, exchange.address, chaiBridge.address, + dydxBridge.address, ); await CoordinatorContract.deployFrom0xArtifactAsync( From 865a253eb5cc912b68faf53b1be48be42c28ad3b Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 5 Feb 2020 01:49:47 -0500 Subject: [PATCH 05/18] `@0x/contracts-utils`: Roll back additions to `LibFractions`. `@0x/contracts-dev-utils`: Add `D18` library for working with base-10, 18-digit decimals. `@0x/contracts-dev-utils`: Use `D18` library instead of `LibFractions` in `LibDydxBalance`. --- contracts/dev-utils/CHANGELOG.json | 4 + .../dev-utils/contracts/src/AssetBalance.sol | 2 +- contracts/dev-utils/contracts/src/D18.sol | 239 ++++++++++++++++++ .../contracts/src/LibDydxBalance.sol | 173 ++----------- contracts/dev-utils/package.json | 2 +- contracts/dev-utils/test/artifacts.ts | 2 + contracts/dev-utils/test/wrappers.ts | 1 + contracts/dev-utils/tsconfig.json | 1 + contracts/utils/CHANGELOG.json | 4 - .../utils/contracts/src/LibFractions.sol | 96 +------ 10 files changed, 278 insertions(+), 246 deletions(-) create mode 100644 contracts/dev-utils/contracts/src/D18.sol diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index 0e8f288e99..03fa998383 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -22,6 +22,10 @@ { "note": "Add `DydxBridge` order validation", "pr": 2466 + }, + { + "note": "Add `D18` library for working with base-10, 18-precision decimals", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/dev-utils/contracts/src/AssetBalance.sol b/contracts/dev-utils/contracts/src/AssetBalance.sol index 45172c757e..ca4dd57a6f 100644 --- a/contracts/dev-utils/contracts/src/AssetBalance.sol +++ b/contracts/dev-utils/contracts/src/AssetBalance.sol @@ -375,7 +375,7 @@ contract AssetBalance is (, , address bridgeAddress, ) = LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); if (bridgeAddress == dydxBridgeAddress) { return ( - LibDydxBalance.getDydxMakerBalance(order, dydxBridgeAddress), + LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()), _MAX_UINT256 ); } diff --git a/contracts/dev-utils/contracts/src/D18.sol b/contracts/dev-utils/contracts/src/D18.sol new file mode 100644 index 0000000000..f491976186 --- /dev/null +++ b/contracts/dev-utils/contracts/src/D18.sol @@ -0,0 +1,239 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; + + +/// @dev A library for working with 18 digit, base 10 decimals. +library D18 { + + /// @dev Decimal places for dydx value quantities. + uint256 private constant PRECISION = 18; + /// @dev 1.0 in base-18 decimal. + int256 private constant DECIMAL_ONE = int256(10 ** PRECISION); + /// @dev Minimum signed integer value. + int256 private constant MIN_INT256_VALUE = int256(0x8000000000000000000000000000000000000000000000000000000000000000); + + /// @dev Return `1.0` + function one() + internal + pure + returns (int256 r) + { + r = DECIMAL_ONE; + } + + /// @dev Add two decimals. + function add(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, b); + } + + /// @dev Add two decimals. + function add(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), b); + } + + /// @dev Add two decimals. + function add(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), int256(b)); + } + + /// @dev Subract two decimals. + function sub(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -int256(b)); + } + + /// @dev Multiply two decimals. + function mul(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _div(_mul(a, b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, int256(b)), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), int256(b)), DECIMAL_ONE); + } + + /// @dev Divide two decimals. + function div(int256 a, int256 b) + internal + pure + returns (int256 r) + { + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); + r = _div(_mul(a, DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, DECIMAL_ONE), int256(b)); + } + + /// @dev Divide two decimals. + function div(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(uint256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), int256(b)); + } + + /// @dev Safely convert an unsigned integer into a signed integer. + function toSigned(uint256 a) + internal + pure + returns (int256 r) + { + require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = int256(a); + } + + /// @dev Clip a signed value to be positive. + function clip(int256 a) + internal + pure + returns (int256 r) + { + r = a < 0 ? 0 : a; + } + + /// @dev Safely multiply two signed integers. + function _mul(int256 a, int256 b) + private + pure + returns (int256 r) + { + if (a == 0 || b == 0) { + return 0; + } + r = a * b; + require(r / a == b && r / b == a, "D18/DECIMAL_MUL_OVERFLOW"); + return r; + } + + /// @dev Safely divide two signed integers. + function _div(int256 a, int256 b) + private + pure + returns (int256 r) + { + require(b != 0, "D18/DECIMAL_DIVISION_BY_ZERO"); + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); + r = a / b; + } + + /// @dev Safely add two signed integers. + function _add(int256 a, int256 b) + private + pure + returns (int256 r) + { + r = a + b; + require( + !((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)), + "D18/DECIMAL_MUL_OVERFLOW" + ); + } + +} diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index dac436f08d..bf061ac5fa 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -23,30 +23,16 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydxBridge.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IDydx.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibFractions.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "./LibAssetData.sol"; +import "./D18.sol"; -// solhint-disable separate-by-one-line-in-contract library LibDydxBalance { using LibBytes for bytes; - using LibSafeMath for uint256; - - /// @dev Decimal places for dydx value quantities. - uint256 private constant DYDX_UNITS_DECIMALS = 18; - /// @dev Base units for dydx value quantities. - uint256 private constant DYDX_UNITS_BASE = 10 ** DYDX_UNITS_DECIMALS; - - /// @dev A fraction/rate. - struct Fraction { - uint256 n; - uint256 d; - } /// @dev Structure that holds all pertinent info needed to perform a balance /// check. @@ -82,7 +68,7 @@ library LibDydxBalance { } // If the rate we withdraw maker tokens is < 1, the asset proxy will // throw because we will always transfer less maker tokens than asked. - if (_ltf(_getMakerTokenWithdrawRate(info), Fraction(1, 1))) { + if (_getMakerTokenWithdrawRate(info) < D18.one()) { return 0; } // The maker balance is the smaller of: @@ -153,7 +139,7 @@ library LibDydxBalance { function _getMakerTokenWithdrawRate(BalanceCheckInfo memory info) internal pure - returns (Fraction memory makerTokenWithdrawRate) + returns (int256 makerTokenWithdrawRate) { // The last action is always a withdraw for the maker token. IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; @@ -169,7 +155,7 @@ library LibDydxBalance { { depositableMakerAmount = uint256(-1); // The conversion rate from maker -> taker. - Fraction memory makerToTakerRate = Fraction( + int256 makerToTakerRate = D18.div( info.takerAssetAmount, info.makerAssetAmount ); @@ -180,22 +166,17 @@ library LibDydxBalance { if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { continue; } - Fraction memory depositRate = _getActionRate(action); + int256 depositRate = _getActionRate(action); // Taker tokens will be transferred to the maker for every fill, so // we reduce the effective deposit rate if we're depositing the taker // token. address depositToken = info.dydx.getMarketTokenAddress(action.marketId); if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { - // `depositRate = max(0, depositRate - makerToTakerRate)` - if (_ltf(makerToTakerRate, depositRate)) { - depositRate = _subf(depositRate, makerToTakerRate); - } else { - depositRate = Fraction(0, 1); - } + depositRate = D18.sub(depositRate, makerToTakerRate); } // If the deposit rate is > 0, we are limited by the transferrable // token balance of the maker. - if (_gtf(depositRate, Fraction(0, 1))) { + if (depositRate > 0) { uint256 supply = _getTransferabeTokenAmount( depositToken, info.makerAddress, @@ -203,11 +184,7 @@ library LibDydxBalance { ); depositableMakerAmount = LibSafeMath.min256( depositableMakerAmount, - LibMath.getPartialAmountFloor( - depositRate.d, - depositRate.n, - supply - ) + uint256(D18.div(supply, depositRate)) ); } } @@ -225,15 +202,13 @@ library LibDydxBalance { assert(info.actions.length >= 1); IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw); - Fraction memory minCr = _getMinimumCollateralizationRatio(info.dydx); - // CR < 1 will cause math underflows. - require(_gtef(minCr, Fraction(1, 1)), "DevUtils/MIN_CR_MUST_BE_GTE_ONE"); + int256 minCr = _getMinimumCollateralizationRatio(info.dydx); // Loop through the accounts. for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) { (uint256 supplyValue, uint256 borrowValue) = _getAccountValues(info, info.accounts[accountIdx]); // All accounts must currently be solvent. - if (borrowValue != 0 && _ltf(Fraction(supplyValue, borrowValue), minCr)) { + if (borrowValue != 0 && D18.div(supplyValue, borrowValue) < minCr) { return 0; } // If this is the same account used to in the withdraw/borrow action, @@ -243,12 +218,12 @@ library LibDydxBalance { } // Compute the deposit/collateralization rate, which is the rate at // which (USD) value is added to the account across all markets. - Fraction memory dd = Fraction(0, 1); + int256 dd = 0; for (uint256 i = 0; i < info.actions.length - 1; ++i) { IDydxBridge.BridgeAction memory deposit = info.actions[i]; assert(deposit.actionType == IDydxBridge.BridgeActionType.Deposit); if (deposit.accountIdx == accountIdx) { - dd = _addf( + dd = D18.add( dd, _toQuoteValue( info.dydx, @@ -260,14 +235,14 @@ library LibDydxBalance { } // Compute the borrow/withdraw rate, which is the rate at which // (USD) value is deducted from the account. - Fraction memory db = _toQuoteValue( + int256 db = _toQuoteValue( info.dydx, withdraw.marketId, _getActionRate(withdraw) ); // If the deposit to withdraw ratio is >= the minimum collateralization // rate, then we will never become insolvent at these prices. - if (_gtef(_divf(dd, db), minCr)) { + if (D18.div(dd, db) >= minCr) { continue; } // The collateralization ratio for this account, parameterized by @@ -275,18 +250,13 @@ library LibDydxBalance { // `cr = (supplyValue + t * dd) / (borrowValue + t * db)` // Solving for `t` gives us: // `t = (supplyValue - cr * borrowValue) / (cr * db - dd)` - // TODO(dorothy-zbornak): It'll also revert when getting extremely - // close to the minimum collateralization ratio. - Fraction memory t = _divf( - _subf( - Fraction(supplyValue, DYDX_UNITS_BASE), - _mulf(minCr, Fraction(borrowValue, DYDX_UNITS_BASE)) - ), - _subf(_mulf(minCr, db), dd) + int256 t = D18.div( + D18.sub(supplyValue, D18.mul(minCr, borrowValue)), + D18.sub(D18.mul(minCr, db), dd) ); solventMakerAmount = LibSafeMath.min256( solventMakerAmount, - t.n.safeDiv(t.d) + uint256(D18.clip(t)) ); } } @@ -322,15 +292,13 @@ library LibDydxBalance { function _getActionRate(IDydxBridge.BridgeAction memory action) private pure - returns (Fraction memory rate) + returns (int256 rate) { rate = action.conversionRateDenominator == 0 - ? Fraction(1, 1) - : _normalizef( - Fraction( - action.conversionRateNumerator, - action.conversionRateDenominator - ) + ? D18.one() + : D18.div( + action.conversionRateNumerator, + action.conversionRateDenominator ); } @@ -340,35 +308,24 @@ library LibDydxBalance { function _getMinimumCollateralizationRatio(IDydx dydx) private view - returns (Fraction memory ratio) + returns (int256 ratio) { IDydx.RiskParams memory riskParams = dydx.getRiskParams(); - return _normalizef( - Fraction( - riskParams.marginRatio.value, - DYDX_UNITS_BASE - ) - ); + return D18.toSigned(riskParams.marginRatio.value); } /// @dev Get the quote (USD) value of a rate within a market. /// @param dydx The Dydx interface. /// @param marketId Dydx market ID. /// @param rate Rate to scale by price. - function _toQuoteValue(IDydx dydx, uint256 marketId, Fraction memory rate) + function _toQuoteValue(IDydx dydx, uint256 marketId, int256 rate) private view - returns (Fraction memory quotedRate) + returns (int256 quotedRate) { IDydx.Price memory price = dydx.getMarketPrice(marketId); uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId)); - return _mulf( - Fraction( - price.value, - 10 ** uint256(DYDX_UNITS_DECIMALS + tokenDecimals) - ), - rate - ); + return D18.mul(D18.div(price.value, 10 ** uint256(tokenDecimals)), rate); } /// @dev Get the total supply and borrow values for an account across all markets. @@ -406,78 +363,4 @@ library LibDydxBalance { LibERC20Token.balanceOf(tokenAddress, owner) ); } - - /*** Fraction helpers ***/ - - /// @dev Check if `a < b`. - function _ltf(Fraction memory a, Fraction memory b) - private - pure - returns (bool isLessThan) - { - return LibFractions.cmp(a.n, a.d, b.n, b.d) == -1; - } - - /// @dev Check if `a > b`. - function _gtf(Fraction memory a, Fraction memory b) - private - pure - returns (bool isGreaterThan) - { - return LibFractions.cmp(a.n, a.d, b.n, b.d) == 1; - } - - /// @dev Check if `a >= b`. - function _gtef(Fraction memory a, Fraction memory b) - private - pure - returns (bool isGreaterThanOrEqual) - { - return !_ltf(a, b); - } - - /// @dev Compute `a + b`. - function _addf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.add(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a - b`. - function _subf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.sub(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a * b`. - function _mulf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.mul(a.n, a.d, b.n, b.d); - } - - /// @dev Compute `a / b`. - function _divf(Fraction memory a, Fraction memory b) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.mul(a.n, a.d, b.d, b.n); - } - - /// @dev Normalize a fraction to prevent arithmetic overflows. - function _normalizef(Fraction memory f) - private - pure - returns (Fraction memory r) - { - (r.n, r.d) = LibFractions.normalize(f.n, f.d); - } } diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index b3cf58b3ee..3dd5b99b23 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -28,7 +28,7 @@ }, "config": { "publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder", - "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", + "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|D18|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/test/artifacts.ts b/contracts/dev-utils/test/artifacts.ts index e4c1802bc0..a4ff20758e 100644 --- a/contracts/dev-utils/test/artifacts.ts +++ b/contracts/dev-utils/test/artifacts.ts @@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types'; import * as Addresses from '../test/generated-artifacts/Addresses.json'; import * as AssetBalance from '../test/generated-artifacts/AssetBalance.json'; +import * as D18 from '../test/generated-artifacts/D18.json'; import * as DevUtils from '../test/generated-artifacts/DevUtils.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json'; @@ -21,6 +22,7 @@ import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBala export const artifacts = { Addresses: Addresses as ContractArtifact, AssetBalance: AssetBalance as ContractArtifact, + D18: D18 as ContractArtifact, DevUtils: DevUtils as ContractArtifact, EthBalanceChecker: EthBalanceChecker as ContractArtifact, ExternalFunctions: ExternalFunctions as ContractArtifact, diff --git a/contracts/dev-utils/test/wrappers.ts b/contracts/dev-utils/test/wrappers.ts index 01f291fa09..587e4c6281 100644 --- a/contracts/dev-utils/test/wrappers.ts +++ b/contracts/dev-utils/test/wrappers.ts @@ -5,6 +5,7 @@ */ export * from '../test/generated-wrappers/addresses'; export * from '../test/generated-wrappers/asset_balance'; +export * from '../test/generated-wrappers/d18'; export * from '../test/generated-wrappers/dev_utils'; export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/external_functions'; diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index eccc9bf633..b2b229e0c7 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -10,6 +10,7 @@ "generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/Addresses.json", "test/generated-artifacts/AssetBalance.json", + "test/generated-artifacts/D18.json", "test/generated-artifacts/DevUtils.json", "test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/ExternalFunctions.json", diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index 3551b3a3d1..251907e9c5 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -37,10 +37,6 @@ { "note": "Export `EvmBytecodeOutputLinkReferences` type.", "pr": 2462 - }, - { - "note": "Add more functions to `LibFractions`.", - "pr": 2466 } ], "timestamp": 1580811564 diff --git a/contracts/utils/contracts/src/LibFractions.sol b/contracts/utils/contracts/src/LibFractions.sol index 1582733988..f5a05706c6 100644 --- a/contracts/utils/contracts/src/LibFractions.sol +++ b/contracts/utils/contracts/src/LibFractions.sol @@ -37,101 +37,7 @@ library LibFractions { .safeMul(d2) .safeAdd(n2.safeMul(d1)); denominator = d1.safeMul(d2); - return normalize(numerator, denominator); - } - - /// @dev Safely subracts two fractions `n1/d1 - n2/d2` - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return numerator Numerator of sum - /// @return denominator Denominator of sum - function sub( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns ( - uint256 numerator, - uint256 denominator - ) - { - if (n2 == 0) { - return (numerator = n1, denominator = d1); - } - numerator = n1 - .safeMul(d2) - .safeSub(n2.safeMul(d1)); - denominator = d1.safeMul(d2); - return normalize(numerator, denominator); - } - - /// @dev Multiply two fractions. - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return numerator numerator of product. - /// @return denominator numerator of product. - function mul( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns ( - uint256 numerator, - uint256 denominator - ) - { - return normalize(n1.safeMul(n2), d1.safeMul(d2)); - } - - /// @dev Compares two fractions. - /// @param n1 numerator of `1` - /// @param d1 denominator of `1` - /// @param n2 numerator of `2` - /// @param d2 denominator of `2` - /// @return compareResult - /// `-1` if `n1/d1 < n2/d2`. - /// `0` if `n1/d1 == n2/d2`. - /// `1` if `n1/d1 > n2/d2`. - function cmp( - uint256 n1, - uint256 d1, - uint256 n2, - uint256 d2 - ) - internal - pure - returns (int8 compareResult) - { - // Handle infinities. - if (d1 == 0) { - if (d2 == 0) { - return 0; - } - return 1; - } else { - if (d2 == 0) { - return -1; - } - } - uint256 nd1 = n1.safeMul(d2); - uint256 nd2 = n2.safeMul(d1); - if (nd1 > nd2) { - return 1; - } - if (nd1 < nd2) { - return -1; - } - return 0; + return (numerator, denominator); } /// @dev Rescales a fraction to prevent overflows during addition if either From 162b6f1a74893f2fc7061ae3907c9f6cce027b46 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 5 Feb 2020 12:14:57 -0500 Subject: [PATCH 06/18] `@0x/dev-utils`: Fix dydx min CR not being `+1`. `@0x/dev-utils`: Fix withdraw rate vs order conversion rate check . --- .../contracts/src/LibDydxBalance.sol | 25 ++++----- .../dev-utils/test/lib_dydx_balance_test.ts | 51 ++++++++++--------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index bf061ac5fa..2cff51812e 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -42,8 +42,7 @@ library LibDydxBalance { address makerAddress; address makerTokenAddress; address takerTokenAddress; - uint256 makerAssetAmount; - uint256 takerAssetAmount; + int256 orderTakerToMakerRate; uint256[] accounts; IDydxBridge.BridgeAction[] actions; } @@ -66,9 +65,10 @@ library LibDydxBalance { if (!_areActionsWellFormed(info)) { return 0; } - // If the rate we withdraw maker tokens is < 1, the asset proxy will - // throw because we will always transfer less maker tokens than asked. - if (_getMakerTokenWithdrawRate(info) < D18.one()) { + // If the rate we withdraw maker tokens is less than the order conversion + // rate , the asset proxy will throw because we will always transfer + // less maker tokens than asked. + if (_getMakerTokenWithdrawRate(info) < info.orderTakerToMakerRate) { return 0; } // The maker balance is the smaller of: @@ -154,11 +154,7 @@ library LibDydxBalance { returns (uint256 depositableMakerAmount) { depositableMakerAmount = uint256(-1); - // The conversion rate from maker -> taker. - int256 makerToTakerRate = D18.div( - info.takerAssetAmount, - info.makerAssetAmount - ); + int256 orderMakerToTakerRate = D18.div(D18.one(), info.orderTakerToMakerRate); // Take the minimum maker amount from all deposits. for (uint256 i = 0; i < info.actions.length; ++i) { IDydxBridge.BridgeAction memory action = info.actions[i]; @@ -172,7 +168,7 @@ library LibDydxBalance { // token. address depositToken = info.dydx.getMarketTokenAddress(action.marketId); if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { - depositRate = D18.sub(depositRate, makerToTakerRate); + depositRate = D18.sub(depositRate, orderMakerToTakerRate); } // If the deposit rate is > 0, we are limited by the transferrable // token balance of the maker. @@ -241,7 +237,7 @@ library LibDydxBalance { _getActionRate(withdraw) ); // If the deposit to withdraw ratio is >= the minimum collateralization - // rate, then we will never become insolvent at these prices. + // ratio, then we will never become insolvent at these prices. if (D18.div(dd, db) >= minCr) { continue; } @@ -279,8 +275,7 @@ library LibDydxBalance { (, info.takerTokenAddress) = LibAssetData.decodeERC20AssetData(order.takerAssetData); } - info.makerAssetAmount = order.makerAssetAmount; - info.takerAssetAmount = order.takerAssetAmount; + info.orderTakerToMakerRate = D18.div(order.makerAssetAmount, order.takerAssetAmount); (IDydxBridge.BridgeData memory bridgeData) = abi.decode(rawBridgeData, (IDydxBridge.BridgeData)); info.accounts = bridgeData.accountNumbers; @@ -311,7 +306,7 @@ library LibDydxBalance { returns (int256 ratio) { IDydx.RiskParams memory riskParams = dydx.getRiskParams(); - return D18.toSigned(riskParams.marginRatio.value); + return D18.add(D18.one(), D18.toSigned(riskParams.marginRatio.value)); } /// @dev Get the quote (USD) value of a rate within a market. diff --git a/contracts/dev-utils/test/lib_dydx_balance_test.ts b/contracts/dev-utils/test/lib_dydx_balance_test.ts index 04438f8bc7..9ca1775d27 100644 --- a/contracts/dev-utils/test/lib_dydx_balance_test.ts +++ b/contracts/dev-utils/test/lib_dydx_balance_test.ts @@ -53,7 +53,7 @@ blockchainTests('LibDydxBalance', env => { const INSOLVENT_ACCOUNT_IDX = 2; const ZERO_BALANCE_ACCOUNT_IDX = 3; const DYDX_CONFIG: TestDydxConfig = { - marginRatio: fromTokenUnitAmount(MARGIN_RATIO, PRICE_DECIMALS), + marginRatio: fromTokenUnitAmount(MARGIN_RATIO - 1, PRICE_DECIMALS), operators: [{ owner: ACCOUNT_OWNER, operator: BRIDGE_ADDRESS }], accounts: [ { @@ -148,8 +148,7 @@ blockchainTests('LibDydxBalance', env => { makerAddress: string; makerTokenAddress: string; takerTokenAddress: string; - makerAssetAmount: BigNumber; - takerAssetAmount: BigNumber; + orderTakerToMakerRate: BigNumber; accounts: BigNumber[]; actions: DydxBridgeAction[]; } @@ -161,8 +160,9 @@ blockchainTests('LibDydxBalance', env => { makerAddress: ACCOUNT_OWNER, makerTokenAddress: DYDX_CONFIG.markets[1].token, takerTokenAddress: DYDX_CONFIG.markets[0].token, - makerAssetAmount: fromTokenUnitAmount(10, MAKER_DECIMALS), - takerAssetAmount: fromTokenUnitAmount(5, TAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(5, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + ), accounts: [DYDX_CONFIG.accounts[SOLVENT_ACCOUNT_IDX].accountId], actions: [], ...fields, @@ -537,8 +537,9 @@ blockchainTests('LibDydxBalance', env => { blockchainTests.resets('_getDepositableMakerAmount()', () => { it('returns infinite if no deposit action', async () => { const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + ), actions: [], }); const makerAssetFillAmount = await testContract.getDepositableMakerAmount(checkInfo).callAsync(); @@ -547,8 +548,9 @@ blockchainTests('LibDydxBalance', env => { it('returns infinite if deposit rate is zero', async () => { const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + ), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -565,8 +567,9 @@ blockchainTests('LibDydxBalance', env => { it('returns infinite if taker tokens cover the deposit rate', async () => { const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + ), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -586,8 +589,9 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(1, MAKER_DECIMALS).div(fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS)), + ), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -619,8 +623,7 @@ blockchainTests('LibDydxBalance', env => { const checkInfo = createBalanceCheckInfo({ // The `takerTokenAddress` will be zero if the asset is not an ERC20. takerTokenAddress: constants.NULL_ADDRESS, - takerAssetAmount: new BigNumber(1), - makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(100, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -652,8 +655,9 @@ blockchainTests('LibDydxBalance', env => { takerTokenAddress: randomAddress(), // These amounts should be effectively ignored in the final computation // because the token being deposited is not the taker token. - takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + ), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -672,8 +676,9 @@ blockchainTests('LibDydxBalance', env => { // The taker tokens getting exchanged in will only partially cover the deposit. const exchangeRate = 0.1; const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(1, MAKER_DECIMALS).div(fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS)), + ), actions: [ // Technically, deposits of the same token are not allowed, but the // check isn't done in this function so we'll do this to simulate @@ -728,8 +733,7 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -756,8 +760,7 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - takerAssetAmount: fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS), - makerAssetAmount: fromTokenUnitAmount(1, MAKER_DECIMALS), + orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -1085,7 +1088,7 @@ blockchainTests('LibDydxBalance', env => { senderAddress: constants.NULL_ADDRESS, makerFee: getRandomInteger(1, constants.MAX_UINT256), takerFee: getRandomInteger(1, constants.MAX_UINT256), - makerAssetAmount: fromTokenUnitAmount(100, TAKER_DECIMALS), + makerAssetAmount: fromTokenUnitAmount(100, MAKER_DECIMALS), takerAssetAmount: fromTokenUnitAmount(10, TAKER_DECIMALS), makerAssetData: createBridgeAssetData(makerTokenAddress, BRIDGE_ADDRESS), takerAssetData: createERC20AssetData(takerTokenAddress), From 651e94bd94a02ca0e9b661ff3d0c60227a685b48 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Thu, 6 Feb 2020 01:01:03 -0500 Subject: [PATCH 07/18] `@0x/asset-proxy`: Add more functions to `IDydx`. `@0x/dev-utils`: Fix all the weird dydx base unit madness. --- contracts/asset-proxy/CHANGELOG.json | 4 + .../contracts/src/bridges/DydxBridge.sol | 4 +- .../contracts/src/interfaces/IDydx.sol | 16 ++- .../contracts/test/TestDydxBridge.sol | 5 +- contracts/asset-proxy/src/index.ts | 9 +- .../contracts/src/LibDydxBalance.sol | 119 ++++++++++++------ .../dev-utils/contracts/test/TestDydx.sol | 36 ++++-- .../dev-utils/test/lib_dydx_balance_test.ts | 45 +++---- 8 files changed, 159 insertions(+), 79 deletions(-) diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index 874141201b..a8342ec103 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -30,6 +30,10 @@ { "note": "Add asset data decoding functions", "pr": 2462 + }, + { + "note": "Add `setOperators()` to `IDydx`", + "pr": "TODO" } ], "timestamp": 1581204851 diff --git a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol index 70c97f268f..8176c2507c 100644 --- a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol +++ b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol @@ -196,7 +196,7 @@ contract DydxBridge is otherAddress: depositFrom, // deposit from the account owner. // unused parameters secondaryMarketId: 0, - otherAccountId: 0, + otherAccountIdx: 0, data: hex'' }); } @@ -234,7 +234,7 @@ contract DydxBridge is otherAddress: withdrawTo, // withdraw tokens to this address. // unused parameters secondaryMarketId: 0, - otherAccountId: 0, + otherAccountIdx: 0, data: hex'' }); } diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol index d784bde2cc..2f2296bd88 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol @@ -50,7 +50,7 @@ interface IDydx { uint256 primaryMarketId; uint256 secondaryMarketId; address otherAddress; - uint256 otherAccountId; + uint256 otherAccountIdx; bytes data; } @@ -83,6 +83,11 @@ interface IDydx { uint256 value; } + struct OperatorArg { + address operator; + bool trusted; + } + /// @dev The global risk parameters that govern the health and security of the system struct RiskParams { // Required ratio of over-collateralization @@ -166,4 +171,13 @@ interface IDydx { external view returns (Value memory supplyValue, Value memory borrowValue); + + // @dev Approves/disapproves any number of operators. An operator is an external address that has the + // same permissions to manipulate an account as the owner of the account. Operators are simply + // addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts. + // Operators are also able to act as AutoTrader contracts on behalf of the account owner if the + // operator is a smart contract and implements the IAutoTrader interface. + // @param args A list of OperatorArgs which have an address and a boolean. The boolean value + // denotes whether to approve (true) or revoke approval (false) for that address. + function setOperators(OperatorArg[] calldata args) external; } diff --git a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol index 895e31231b..9345e21cdc 100644 --- a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol +++ b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol @@ -129,7 +129,7 @@ contract TestDydxBridge is actions[i].primaryMarketId, actions[i].secondaryMarketId, actions[i].otherAddress, - actions[i].otherAccountId, + actions[i].otherAccountIdx, actions[i].data ); @@ -216,6 +216,9 @@ contract TestDydxBridge is returns (Value memory supplyValue, Value memory borrowValue) {} + /// @dev Unused. + function setOperators(OperatorArg[] calldata args) external {} + /// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address. function _getDydxAddress() internal diff --git a/contracts/asset-proxy/src/index.ts b/contracts/asset-proxy/src/index.ts index 155cc1b51e..89d3053fa6 100644 --- a/contracts/asset-proxy/src/index.ts +++ b/contracts/asset-proxy/src/index.ts @@ -1,21 +1,22 @@ export { artifacts } from './artifacts'; export { + ChaiBridgeContract, ERC1155ProxyContract, ERC20BridgeProxyContract, ERC20ProxyContract, ERC721ProxyContract, Eth2DaiBridgeContract, DydxBridgeContract, - TestDydxBridgeContract, IAssetDataContract, IAssetProxyContract, + IChaiContract, + IDydxContract, + KyberBridgeContract, MultiAssetProxyContract, StaticCallProxyContract, + TestDydxBridgeContract, TestStaticCallTargetContract, UniswapBridgeContract, - KyberBridgeContract, - ChaiBridgeContract, - IChaiContract, } from './wrappers'; export { ERC20Wrapper } from './erc20_wrapper'; diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index 2cff51812e..20095ef30e 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -33,6 +33,12 @@ import "./D18.sol"; library LibDydxBalance { using LibBytes for bytes; + using LibSafeMath for uint256; + + /// @dev Padding % added to the minimum collateralization ratio to + /// prevent withdrawing exactly the amount that would make an account + /// insolvent. 1 bps. + int256 private constant MARGIN_RATIO_PADDING = 0.0001e18; /// @dev Structure that holds all pertinent info needed to perform a balance /// check. @@ -42,7 +48,7 @@ library LibDydxBalance { address makerAddress; address makerTokenAddress; address takerTokenAddress; - int256 orderTakerToMakerRate; + int256 orderMakerToTakerRate; uint256[] accounts; IDydxBridge.BridgeAction[] actions; } @@ -65,10 +71,10 @@ library LibDydxBalance { if (!_areActionsWellFormed(info)) { return 0; } - // If the rate we withdraw maker tokens is less than the order conversion - // rate , the asset proxy will throw because we will always transfer - // less maker tokens than asked. - if (_getMakerTokenWithdrawRate(info) < info.orderTakerToMakerRate) { + // If the rate we withdraw maker tokens is less than one, the asset + // proxy will throw because we will always transfer less maker tokens + // than asked. + if (_getMakerTokenWithdrawRate(info) < D18.one()) { return 0; } // The maker balance is the smaller of: @@ -154,7 +160,6 @@ library LibDydxBalance { returns (uint256 depositableMakerAmount) { depositableMakerAmount = uint256(-1); - int256 orderMakerToTakerRate = D18.div(D18.one(), info.orderTakerToMakerRate); // Take the minimum maker amount from all deposits. for (uint256 i = 0; i < info.actions.length; ++i) { IDydxBridge.BridgeAction memory action = info.actions[i]; @@ -162,13 +167,15 @@ library LibDydxBalance { if (action.actionType != IDydxBridge.BridgeActionType.Deposit) { continue; } + // `depositRate` is the rate at which we convert a maker token into + // a taker token for deposit. int256 depositRate = _getActionRate(action); // Taker tokens will be transferred to the maker for every fill, so // we reduce the effective deposit rate if we're depositing the taker // token. address depositToken = info.dydx.getMarketTokenAddress(action.marketId); if (info.takerTokenAddress != address(0) && depositToken == info.takerTokenAddress) { - depositRate = D18.sub(depositRate, orderMakerToTakerRate); + depositRate = D18.sub(depositRate, info.orderMakerToTakerRate); } // If the deposit rate is > 0, we are limited by the transferrable // token balance of the maker. @@ -198,11 +205,11 @@ library LibDydxBalance { assert(info.actions.length >= 1); IDydxBridge.BridgeAction memory withdraw = info.actions[info.actions.length - 1]; assert(withdraw.actionType == IDydxBridge.BridgeActionType.Withdraw); - int256 minCr = _getMinimumCollateralizationRatio(info.dydx); + int256 minCr = D18.add(_getMinimumCollateralizationRatio(info.dydx), MARGIN_RATIO_PADDING); // Loop through the accounts. for (uint256 accountIdx = 0; accountIdx < info.accounts.length; ++accountIdx) { (uint256 supplyValue, uint256 borrowValue) = - _getAccountValues(info, info.accounts[accountIdx]); + _getAccountMarketValues(info, info.accounts[accountIdx]); // All accounts must currently be solvent. if (borrowValue != 0 && D18.div(supplyValue, borrowValue) < minCr) { return 0; @@ -221,20 +228,18 @@ library LibDydxBalance { if (deposit.accountIdx == accountIdx) { dd = D18.add( dd, - _toQuoteValue( - info.dydx, - deposit.marketId, - _getActionRate(deposit) + _getActionRateValue( + info, + deposit ) ); } } // Compute the borrow/withdraw rate, which is the rate at which // (USD) value is deducted from the account. - int256 db = _toQuoteValue( - info.dydx, - withdraw.marketId, - _getActionRate(withdraw) + int256 db = _getActionRateValue( + info, + withdraw ); // If the deposit to withdraw ratio is >= the minimum collateralization // ratio, then we will never become insolvent at these prices. @@ -252,7 +257,8 @@ library LibDydxBalance { ); solventMakerAmount = LibSafeMath.min256( solventMakerAmount, - uint256(D18.clip(t)) + // `t` is in maker token units, so convert it to maker wei. + _toWei(info.makerTokenAddress, uint256(D18.clip(t))) ); } } @@ -275,14 +281,14 @@ library LibDydxBalance { (, info.takerTokenAddress) = LibAssetData.decodeERC20AssetData(order.takerAssetData); } - info.orderTakerToMakerRate = D18.div(order.makerAssetAmount, order.takerAssetAmount); + info.orderMakerToTakerRate = D18.div(order.takerAssetAmount, order.makerAssetAmount); (IDydxBridge.BridgeData memory bridgeData) = abi.decode(rawBridgeData, (IDydxBridge.BridgeData)); info.accounts = bridgeData.accountNumbers; info.actions = bridgeData.actions; } - /// @dev Returns the conversion rate for an action, treating infinites as 1. + /// @dev Returns the conversion rate for an action. /// @param action A `BridgeAction`. function _getActionRate(IDydxBridge.BridgeAction memory action) private @@ -297,6 +303,59 @@ library LibDydxBalance { ); } + /// @dev Returns the USD value of an action based on its conversion rate + /// and market prices. + /// @param info State from `_getBalanceCheckInfo()`. + /// @param action A `BridgeAction`. + function _getActionRateValue( + BalanceCheckInfo memory info, + IDydxBridge.BridgeAction memory action + ) + private + view + returns (int256 value) + { + address toToken = info.dydx.getMarketTokenAddress(action.marketId); + uint256 fromTokenDecimals = LibERC20Token.decimals(info.makerTokenAddress); + uint256 toTokenDecimals = LibERC20Token.decimals(toToken); + // First express the rate as 18-decimal units. + value = toTokenDecimals > fromTokenDecimals + ? int256( + uint256(_getActionRate(action)) + .safeDiv(10 ** (toTokenDecimals - fromTokenDecimals)) + ) + : int256( + uint256(_getActionRate(action)) + .safeMul(10 ** (fromTokenDecimals - toTokenDecimals)) + ); + // Prices have 18 + (18 - TOKEN_DECIMALS) decimal places because + // consistency is stupid. + uint256 price = info.dydx.getMarketPrice(action.marketId).value; + // Make prices have 18 decimals. + if (toTokenDecimals > 18) { + price = price.safeMul(10 ** (toTokenDecimals - 18)); + } else { + price = price.safeDiv(10 ** (18 - toTokenDecimals)); + } + // The action value is the action rate times the price. + value = D18.mul(price, value); + } + + /// @dev Returns the conversion rate for an action, expressed as units + /// of the market token. + /// @param token Address the of the token. + /// @param units Token units expressed with 18 digit precision. + function _toWei(address token, uint256 units) + private + view + returns (uint256 rate) + { + uint256 decimals = LibERC20Token.decimals(token); + rate = decimals > 18 + ? units.safeMul(10 ** (decimals - 18)) + : units.safeDiv(10 ** (18 - decimals)); + } + /// @dev Get the global minimum collateralization ratio required for /// an account to be considered solvent. /// @param dydx The Dydx interface. @@ -309,24 +368,10 @@ library LibDydxBalance { return D18.add(D18.one(), D18.toSigned(riskParams.marginRatio.value)); } - /// @dev Get the quote (USD) value of a rate within a market. - /// @param dydx The Dydx interface. - /// @param marketId Dydx market ID. - /// @param rate Rate to scale by price. - function _toQuoteValue(IDydx dydx, uint256 marketId, int256 rate) - private - view - returns (int256 quotedRate) - { - IDydx.Price memory price = dydx.getMarketPrice(marketId); - uint8 tokenDecimals = LibERC20Token.decimals(dydx.getMarketTokenAddress(marketId)); - return D18.mul(D18.div(price.value, 10 ** uint256(tokenDecimals)), rate); - } - /// @dev Get the total supply and borrow values for an account across all markets. /// @param info State from `_getBalanceCheckInfo()`. /// @param account The Dydx account identifier. - function _getAccountValues(BalanceCheckInfo memory info, uint256 account) + function _getAccountMarketValues(BalanceCheckInfo memory info, uint256 account) private view returns (uint256 supplyValue, uint256 borrowValue) @@ -336,7 +381,9 @@ library LibDydxBalance { info.makerAddress, account )); - return (supplyValue_.value, borrowValue_.value); + // Account values have 36 decimal places because dydx likes to make sure + // you're paying attention. + return (supplyValue_.value / 1e18, borrowValue_.value / 1e18); } /// @dev Get the amount of an ERC20 token held by `owner` that can be transferred diff --git a/contracts/dev-utils/contracts/test/TestDydx.sol b/contracts/dev-utils/contracts/test/TestDydx.sol index c3c7a62e1c..be251af314 100644 --- a/contracts/dev-utils/contracts/test/TestDydx.sol +++ b/contracts/dev-utils/contracts/test/TestDydx.sol @@ -106,16 +106,6 @@ contract TestDydx { }); } - function getMarketPrice( - uint256 marketId - ) - external - view - returns (IDydx.Price memory price) - { - return IDydx.Price(_markets[marketId].price); - } - function getAdjustedAccountValues( IDydx.AccountInfo calldata account ) @@ -124,11 +114,13 @@ contract TestDydx { returns (IDydx.Value memory supplyValue, IDydx.Value memory borrowValue) { for (uint256 marketId = 0; marketId < _markets.length; ++marketId) { - MarketInfo memory market = _markets[marketId]; int256 balance = _balance[_getBalanceHash(account.owner, account.number, marketId)]; - uint256 decimals = LibERC20Token.decimals(market.token); - balance = balance * int256(market.price) / int256(10 ** decimals); + // Account values have 36 decimal places. + // `getMarketPrice()` returns a unit with + // 18 + (18 - TOKEN_DECIMALS) decimal places so multiplying the price + // with the wei balance will result in a 36 decimal value. + balance = balance * int256(getMarketPrice(marketId).value); if (balance >= 0) { supplyValue.value += uint256(balance); } else { @@ -137,6 +129,24 @@ contract TestDydx { } } + function getMarketPrice( + uint256 marketId + ) + public + view + returns (IDydx.Price memory price) + { + MarketInfo memory market = _markets[marketId]; + uint256 decimals = LibERC20Token.decimals(market.token); + price.value = _markets[marketId].price; + // Market prices have 18 + (18 - TOKEN_DECIMALS) + if (decimals > 18) { + price.value /= 10 ** (decimals - 18); + } else { + price.value *= 10 ** (18 - decimals); + } + } + function _getOperatorHash(address owner, address operator) private pure diff --git a/contracts/dev-utils/test/lib_dydx_balance_test.ts b/contracts/dev-utils/test/lib_dydx_balance_test.ts index 9ca1775d27..6d06adfb56 100644 --- a/contracts/dev-utils/test/lib_dydx_balance_test.ts +++ b/contracts/dev-utils/test/lib_dydx_balance_test.ts @@ -148,7 +148,7 @@ blockchainTests('LibDydxBalance', env => { makerAddress: string; makerTokenAddress: string; takerTokenAddress: string; - orderTakerToMakerRate: BigNumber; + orderMakerToTakerRate: BigNumber; accounts: BigNumber[]; actions: DydxBridgeAction[]; } @@ -160,8 +160,8 @@ blockchainTests('LibDydxBalance', env => { makerAddress: ACCOUNT_OWNER, makerTokenAddress: DYDX_CONFIG.markets[1].token, takerTokenAddress: DYDX_CONFIG.markets[0].token, - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(5, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(10, TAKER_DECIMALS).div(fromTokenUnitAmount(5, MAKER_DECIMALS)), ), accounts: [DYDX_CONFIG.accounts[SOLVENT_ACCOUNT_IDX].accountId], actions: [], @@ -214,8 +214,9 @@ blockchainTests('LibDydxBalance', env => { // Computes a deposit rate that is the minimum to keep an account solvent // perpetually. - function getBalancedDepositRate(withdrawRate: BigNumber, scaling: Numberish = 1.000001): BigNumber { - return withdrawRate.times((MAKER_PRICE / TAKER_PRICE) * MARGIN_RATIO).times(scaling); + function getBalancedDepositRate(withdrawRate: BigNumber, scaling: Numberish = 1): BigNumber { + // Add a small amount to the margin ratio to stay just above insolvency. + return withdrawRate.times((MAKER_PRICE / TAKER_PRICE) * (MARGIN_RATIO + 1.1e-4)).times(scaling); } function takerToMakerAmount(takerAmount: BigNumber): BigNumber { @@ -537,8 +538,8 @@ blockchainTests('LibDydxBalance', env => { blockchainTests.resets('_getDepositableMakerAmount()', () => { it('returns infinite if no deposit action', async () => { const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(10, TAKER_DECIMALS).div(fromTokenUnitAmount(100, MAKER_DECIMALS)), ), actions: [], }); @@ -548,8 +549,8 @@ blockchainTests('LibDydxBalance', env => { it('returns infinite if deposit rate is zero', async () => { const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(10, TAKER_DECIMALS).div(fromTokenUnitAmount(100, MAKER_DECIMALS)), ), actions: [ { @@ -567,8 +568,8 @@ blockchainTests('LibDydxBalance', env => { it('returns infinite if taker tokens cover the deposit rate', async () => { const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(10, TAKER_DECIMALS).div(fromTokenUnitAmount(100, MAKER_DECIMALS)), ), actions: [ { @@ -589,8 +590,8 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(1, MAKER_DECIMALS).div(fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS).div(fromTokenUnitAmount(1, MAKER_DECIMALS)), ), actions: [ { @@ -623,7 +624,7 @@ blockchainTests('LibDydxBalance', env => { const checkInfo = createBalanceCheckInfo({ // The `takerTokenAddress` will be zero if the asset is not an ERC20. takerTokenAddress: constants.NULL_ADDRESS, - orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(100, MAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount(fromTokenUnitAmount(0.1, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -655,8 +656,8 @@ blockchainTests('LibDydxBalance', env => { takerTokenAddress: randomAddress(), // These amounts should be effectively ignored in the final computation // because the token being deposited is not the taker token. - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(100, MAKER_DECIMALS).div(fromTokenUnitAmount(10, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(10, TAKER_DECIMALS).div(fromTokenUnitAmount(100, MAKER_DECIMALS)), ), actions: [ { @@ -676,8 +677,8 @@ blockchainTests('LibDydxBalance', env => { // The taker tokens getting exchanged in will only partially cover the deposit. const exchangeRate = 0.1; const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount( - fromTokenUnitAmount(1, MAKER_DECIMALS).div(fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount( + fromTokenUnitAmount(exchangeRate, TAKER_DECIMALS).div(fromTokenUnitAmount(1, MAKER_DECIMALS)), ), actions: [ // Technically, deposits of the same token are not allowed, but the @@ -733,7 +734,7 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -760,7 +761,7 @@ blockchainTests('LibDydxBalance', env => { const exchangeRate = 0.1; const depositRate = Math.random() + exchangeRate; const checkInfo = createBalanceCheckInfo({ - orderTakerToMakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), + orderMakerToTakerRate: fromTokenUnitAmount(fromTokenUnitAmount(1 / exchangeRate, MAKER_DECIMALS)), actions: [ { actionType: DydxBridgeActionType.Deposit, @@ -1155,8 +1156,8 @@ blockchainTests('LibDydxBalance', env => { actionType: DydxBridgeActionType.Withdraw, accountIdx: new BigNumber(0), marketId: new BigNumber(1), - conversionRateNumerator: new BigNumber(9e18), - conversionRateDenominator: new BigNumber(10e18), + conversionRateNumerator: new BigNumber(0.99e18), + conversionRateDenominator: new BigNumber(1e18), }, ], }), From bd9c9cedcaf24c4593da2750dc4ade5836916a14 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Thu, 6 Feb 2020 01:21:27 -0500 Subject: [PATCH 08/18] `@0x/contracts-test-utils`: Add `blockchainTests.config` --- contracts/test-utils/src/mocha_blockchain.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contracts/test-utils/src/mocha_blockchain.ts b/contracts/test-utils/src/mocha_blockchain.ts index 9491907e71..061fecd3e7 100644 --- a/contracts/test-utils/src/mocha_blockchain.ts +++ b/contracts/test-utils/src/mocha_blockchain.ts @@ -20,11 +20,22 @@ export interface ContextDefinition extends mocha.IContextDefinition { optional: ContextDefinitionCallback; } +/** + * `blockchainTests()` config options. + */ +export interface BlockchainContextConfig { + fork: Partial<{ + // Accounts to unlock on ganache. + unlockedAccounts: string[]; + }>; +} + /** * Interface for `blockchainTests()`. */ export interface BlockchainContextDefinition { (description: string, callback: BlockchainSuiteCallback): ISuite; + config: Partial; only: BlockchainContextDefinitionCallback; skip: BlockchainContextDefinitionCallback; optional: BlockchainContextDefinitionCallback; @@ -120,10 +131,13 @@ export class ForkedBlockchainTestsEnvironmentSingleton extends BlockchainTestsEn } protected static _createWeb3Provider(forkHost: string): Web3ProviderEngine { + const forkConfig = blockchainTests.config.fork || {}; + const unlockedAccounts = forkConfig.unlockedAccounts; return web3Factory.getRpcProvider({ ...providerConfigs, fork: forkHost, blockTime: 0, + ...(unlockedAccounts ? { unlocked_accounts: unlockedAccounts } : {}), }); } @@ -209,6 +223,7 @@ export const blockchainTests: BlockchainContextDefinition = _.assign( return defineBlockchainSuite(StandardBlockchainTestsEnvironmentSingleton, description, callback, describe); }, { + config: {}, only(description: string, callback: BlockchainSuiteCallback): ISuite { return defineBlockchainSuite( StandardBlockchainTestsEnvironmentSingleton, From d94a26f0f41231ef0540b6ddb3684ed9a57d860f Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Thu, 6 Feb 2020 01:25:10 -0500 Subject: [PATCH 09/18] `@0x/contracts-integrations`: Add DydxBridge validation integration tests. --- contracts/integrations/CHANGELOG.json | 4 + .../dev-utils/dydx_order_validation_test.ts | 323 ++++++++++++++++++ contracts/test-utils/CHANGELOG.json | 9 + 3 files changed, 336 insertions(+) create mode 100644 contracts/integrations/test/dev-utils/dydx_order_validation_test.ts diff --git a/contracts/integrations/CHANGELOG.json b/contracts/integrations/CHANGELOG.json index fd221e8db4..035d0a471e 100644 --- a/contracts/integrations/CHANGELOG.json +++ b/contracts/integrations/CHANGELOG.json @@ -35,6 +35,10 @@ { "note": "Add DydxBridge validation", "pr": 2466 + }, + { + "note": "Add DevUtils DydxBridge validation mainnet tests", + "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts new file mode 100644 index 0000000000..79d4ce2226 --- /dev/null +++ b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts @@ -0,0 +1,323 @@ +import { + artifacts as assetProxyArtifacts, + DydxBridgeActionType, + DydxBridgeContract, + DydxBridgeData, + dydxBridgeDataEncoder, + encodeERC20AssetData, + encodeERC20BridgeAssetData, + IDydxContract, +} from '@0x/contracts-asset-proxy'; +import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils'; +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { blockchainTests, constants, expect, Numberish } from '@0x/contracts-test-utils'; +import { Order } from '@0x/types'; +import { BigNumber, fromTokenUnitAmount, hexUtils, toTokenUnitAmount } from '@0x/utils'; + +import { contractAddresses } from '../mainnet_fork_utils'; + +enum DydxActionType { + Deposit = 0, + Withdraw = 1, +} + +enum DydxAssetDenomination { + Wei = 0, + Par = 1, +} + +enum DydxAssetReference { + Delta = 0, + Target = 1, +} + +const MAKER_ADDRESS = '0x3a9F7C8cA36C42d7035E87C3304eE5cBd353a532'; + +blockchainTests.config = { + fork: { + unlockedAccounts: [MAKER_ADDRESS], + }, +}; + +blockchainTests.fork('DevUtils dydx order validation tests', env => { + const { ZERO_AMOUNT: ZERO } = constants; + const SIGNATURE = '0x01'; // Invalid signature. Doesn't matter. + const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; + const DYDX_ADDRESS = '0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e'; + const TOKEN_INFO: { [addr: string]: { decimals: number; marketId: number } } = { + [DAI_ADDRESS]: { + decimals: 18, + marketId: 3, + }, + [USDC_ADDRESS]: { + decimals: 6, + marketId: 2, + }, + }; + const DAI_DECIMALS = TOKEN_INFO[DAI_ADDRESS].decimals; + const USDC_DECIMALS = TOKEN_INFO[USDC_ADDRESS].decimals; + const DAI_MARKET_ID = TOKEN_INFO[DAI_ADDRESS].marketId; + const USDC_MARKET_ID = TOKEN_INFO[USDC_ADDRESS].marketId; + let bridge: DydxBridgeContract; + let dydx: IDydxContract; + let dai: ERC20TokenContract; + let devUtils: DevUtilsContract; + let accountOwner: string; + let minMarginRatio: number; + + before(async () => { + [accountOwner] = await env.getAccountAddressesAsync(); + dydx = new IDydxContract(DYDX_ADDRESS, env.provider, env.txDefaults); + dai = new ERC20TokenContract(DAI_ADDRESS, env.provider, env.txDefaults); + bridge = await DydxBridgeContract.deployFrom0xArtifactAsync( + assetProxyArtifacts.DydxBridge, + env.provider, + env.txDefaults, + {}, + ); + devUtils = await DevUtilsContract.deployWithLibrariesFrom0xArtifactAsync( + devUtilsArtifacts.DevUtils, + devUtilsArtifacts, + env.provider, + env.txDefaults, + devUtilsArtifacts, + contractAddresses.exchange, + contractAddresses.chaiBridge, + bridge.address, + ); + minMarginRatio = toTokenUnitAmount((await dydx.getRiskParams().callAsync()).marginRatio.value) + .plus(1) + .toNumber(); + // Deposit Dai collateral. + await dai.approve(DYDX_ADDRESS, constants.MAX_UINT256).awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + await dydx + .setOperators([{ operator: bridge.address, trusted: true }]) + .awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + }); + + async function depositAndWithdrawAsync( + accountId: BigNumber, + depositSize: Numberish = 0, + withdrawSize: Numberish = 0, + ): Promise { + await dydx + .operate( + [{ owner: MAKER_ADDRESS, number: accountId }], + [ + ...(depositSize > 0 + ? [ + { + actionType: DydxActionType.Deposit, + accountIdx: ZERO, + amount: { + sign: true, + denomination: DydxAssetDenomination.Wei, + ref: DydxAssetReference.Delta, + value: fromTokenUnitAmount(depositSize, DAI_DECIMALS), + }, + primaryMarketId: new BigNumber(DAI_MARKET_ID), + secondaryMarketId: new BigNumber(constants.NULL_ADDRESS), + otherAddress: MAKER_ADDRESS, + otherAccountIdx: ZERO, + data: constants.NULL_BYTES, + }, + ] + : []), + ...(withdrawSize > 0 + ? [ + { + actionType: DydxActionType.Withdraw, + accountIdx: ZERO, + amount: { + sign: false, + denomination: DydxAssetDenomination.Wei, + ref: DydxAssetReference.Delta, + value: fromTokenUnitAmount(withdrawSize, USDC_DECIMALS), + }, + primaryMarketId: new BigNumber(USDC_MARKET_ID), + secondaryMarketId: new BigNumber(constants.NULL_ADDRESS), + otherAddress: MAKER_ADDRESS, + otherAccountIdx: ZERO, + data: constants.NULL_BYTES, + }, + ] + : []), + ], + ) + .awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + } + + const SECONDS_IN_ONE_YEAR = 365 * 24 * 60 * 60; + + function createOrder(fields: Partial = {}): Order { + return { + chainId: 1, + exchangeAddress: contractAddresses.exchange, + expirationTimeSeconds: new BigNumber(Math.floor(Date.now() / 1000 + SECONDS_IN_ONE_YEAR)), + makerAddress: MAKER_ADDRESS, + takerAddress: constants.NULL_ADDRESS, + senderAddress: constants.NULL_ADDRESS, + feeRecipientAddress: constants.NULL_ADDRESS, + salt: new BigNumber(hexUtils.random()), + makerAssetAmount: fromTokenUnitAmount(100, USDC_DECIMALS), + takerAssetAmount: fromTokenUnitAmount(200, DAI_DECIMALS), + makerFee: ZERO, + takerFee: ZERO, + makerAssetData: encodeDydxBridgeAssetData(), + takerAssetData: encodeERC20AssetData(DAI_ADDRESS), + makerFeeAssetData: constants.NULL_BYTES, + takerFeeAssetData: constants.NULL_BYTES, + ...fields, + }; + } + + function encodeDydxBridgeAssetData( + fields: Partial<{ + fromToken: string; + toToken: string; + depositRate: number; + withdrawRate: number; + accountId: BigNumber; + }> = {}, + ): string { + const { fromToken, toToken, depositRate, withdrawRate, accountId } = { + fromToken: DAI_ADDRESS, + toToken: USDC_ADDRESS, + depositRate: 1, + withdrawRate: 1, + accountId: ZERO, + ...fields, + }; + const fromTokenMarketId = new BigNumber(TOKEN_INFO[fromToken].marketId); + const toTokenMarketId = new BigNumber(TOKEN_INFO[toToken].marketId); + const bridgeData: DydxBridgeData = { + accountNumbers: [accountId], + actions: [ + ...(depositRate > 0 + ? [ + { + actionType: DydxBridgeActionType.Deposit, + accountIdx: ZERO, + marketId: fromTokenMarketId, + ...createConversionFraction(toToken, fromToken, depositRate), + }, + ] + : []), + ...(withdrawRate > 0 + ? [ + { + actionType: DydxBridgeActionType.Withdraw, + accountIdx: ZERO, + marketId: toTokenMarketId, + ...createConversionFraction(toToken, toToken, withdrawRate), + }, + ] + : []), + ], + }; + return encodeERC20BridgeAssetData(toToken, bridge.address, dydxBridgeDataEncoder.encode({ bridgeData })); + } + + // Create fraction with default 18 decimal precision. + function createConversionFraction( + fromToken: string, + toToken: string, + rate: number, + ): { + conversionRateNumerator: BigNumber; + conversionRateDenominator: BigNumber; + } { + const fromDecimals = TOKEN_INFO[fromToken].decimals; + const toDecimals = TOKEN_INFO[toToken].decimals; + return { + conversionRateNumerator: fromTokenUnitAmount(rate, toDecimals), + conversionRateDenominator: fromTokenUnitAmount(1, fromDecimals), + }; + } + + function randomAccountId(): BigNumber { + return new BigNumber(hexUtils.random()); + } + + it('validates a fully solvent order', async () => { + // This account is collateralized enough to fill the order with just + // withdraws. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(accountId, 200, 0); + const order = createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); + }); + + it('validates a perpetually solvent order', async () => { + // This account is not very well collateralized, but the deposit rate + // will keep the collateralization ratio the same or better. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(accountId, 1, 0); + const order = createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); + }); + + it('validates a partially solvent order with an inadequate deposit', async () => { + // This account is not very well collateralized and the deposit rate is + // also too low to sustain the collateralization ratio for the full order. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(accountId, 1, 0); + const order = createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio * 0.95, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + it('validates a partially solvent order with no deposit', async () => { + // This account is not very well collateralized and there is no deposit + // to keep the collateralization ratio up. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(accountId, 1, 0); + const order = createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + // TODO(dorothy-zbornak): We can't actually create an account that's below + // the margin ratio without replacing the price oracles. + it('invalidates a virtually insolvent order', async () => { + // This account has a collateralization ratio JUST above the + // minimum margin ratio, so it can only withdraw nearly zero maker tokens. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(accountId, 1, 1 / (minMarginRatio + 3e-4)); + const order = createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + // Price fluctuations will cause this to be a little above zero, so we + // don't compare to zero. + expect(fillableTakerAssetAmount).to.bignumber.lt(fromTokenUnitAmount(1e-7, DAI_DECIMALS)); + }); +}); diff --git a/contracts/test-utils/CHANGELOG.json b/contracts/test-utils/CHANGELOG.json index 9d5a549283..456fde6f29 100644 --- a/contracts/test-utils/CHANGELOG.json +++ b/contracts/test-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "5.3.0", + "changes": [ + { + "note": "Add `blockchainTests.config`", + "pr": 2466 + } + ] + }, { "timestamp": 1581748629, "version": "5.1.5", From bf9b4b993f9ca9636377cfca23d8cd8d723452a0 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Thu, 6 Feb 2020 01:55:55 -0500 Subject: [PATCH 10/18] `@0x/contracts-test-utils`: Fix `blockchainTests` fork config to work with other tests. --- .../contracts/src/interfaces/IDydx.sol | 18 +++++------ .../contracts/test/TestDydxBridge.sol | 6 ++-- .../dev-utils/dydx_order_validation_test.ts | 4 +-- contracts/test-utils/src/mocha_blockchain.ts | 32 +++++++++++++++++-- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol index 2f2296bd88..8b353ad6ac 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol @@ -117,6 +117,15 @@ interface IDydx { ) external; + // @dev Approves/disapproves any number of operators. An operator is an external address that has the + // same permissions to manipulate an account as the owner of the account. Operators are simply + // addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts. + // Operators are also able to act as AutoTrader contracts on behalf of the account owner if the + // operator is a smart contract and implements the IAutoTrader interface. + // @param args A list of OperatorArgs which have an address and a boolean. The boolean value + // denotes whether to approve (true) or revoke approval (false) for that address. + function setOperators(OperatorArg[] calldata args) external; + /// @dev Return true if a particular address is approved as an operator for an owner's accounts. /// Approved operators can act on the accounts of the owner as if it were the operator's own. /// @param owner The owner of the accounts @@ -171,13 +180,4 @@ interface IDydx { external view returns (Value memory supplyValue, Value memory borrowValue); - - // @dev Approves/disapproves any number of operators. An operator is an external address that has the - // same permissions to manipulate an account as the owner of the account. Operators are simply - // addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts. - // Operators are also able to act as AutoTrader contracts on behalf of the account owner if the - // operator is a smart contract and implements the IAutoTrader interface. - // @param args A list of OperatorArgs which have an address and a boolean. The boolean value - // denotes whether to approve (true) or revoke approval (false) for that address. - function setOperators(OperatorArg[] calldata args) external; } diff --git a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol index 9345e21cdc..ce3f4f8b78 100644 --- a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol +++ b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol @@ -172,6 +172,9 @@ contract TestDydxBridge is return _testTokenAddress; } + /// @dev Unused. + function setOperators(OperatorArg[] calldata args) external {} + /// @dev Unused. function getIsLocalOperator( address owner, @@ -216,9 +219,6 @@ contract TestDydxBridge is returns (Value memory supplyValue, Value memory borrowValue) {} - /// @dev Unused. - function setOperators(OperatorArg[] calldata args) external {} - /// @dev overrides `_getDydxAddress()` from `DeploymentConstants` to return this address. function _getDydxAddress() internal diff --git a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts index 79d4ce2226..9986bd759b 100644 --- a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts +++ b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts @@ -33,11 +33,11 @@ enum DydxAssetReference { const MAKER_ADDRESS = '0x3a9F7C8cA36C42d7035E87C3304eE5cBd353a532'; -blockchainTests.config = { +blockchainTests.configure({ fork: { unlockedAccounts: [MAKER_ADDRESS], }, -}; +}); blockchainTests.fork('DevUtils dydx order validation tests', env => { const { ZERO_AMOUNT: ZERO } = constants; diff --git a/contracts/test-utils/src/mocha_blockchain.ts b/contracts/test-utils/src/mocha_blockchain.ts index 061fecd3e7..b9cf11e6d4 100644 --- a/contracts/test-utils/src/mocha_blockchain.ts +++ b/contracts/test-utils/src/mocha_blockchain.ts @@ -30,12 +30,14 @@ export interface BlockchainContextConfig { }>; } +let TEST_ENV_CONFIG: Partial = {}; + /** * Interface for `blockchainTests()`. */ export interface BlockchainContextDefinition { (description: string, callback: BlockchainSuiteCallback): ISuite; - config: Partial; + configure: (config?: Partial) => void; only: BlockchainContextDefinitionCallback; skip: BlockchainContextDefinitionCallback; optional: BlockchainContextDefinitionCallback; @@ -102,6 +104,11 @@ export class StandardBlockchainTestsEnvironmentSingleton extends BlockchainTests return StandardBlockchainTestsEnvironmentSingleton._instance; } + // Reset the singleton. + public static reset(): void { + StandardBlockchainTestsEnvironmentSingleton._instance = undefined; + } + // Get the singleton instance of this class. public static getInstance(): StandardBlockchainTestsEnvironmentSingleton | undefined { return StandardBlockchainTestsEnvironmentSingleton._instance; @@ -130,8 +137,13 @@ export class ForkedBlockchainTestsEnvironmentSingleton extends BlockchainTestsEn return ForkedBlockchainTestsEnvironmentSingleton._instance; } + // Reset the singleton. + public static reset(): void { + ForkedBlockchainTestsEnvironmentSingleton._instance = undefined; + } + protected static _createWeb3Provider(forkHost: string): Web3ProviderEngine { - const forkConfig = blockchainTests.config.fork || {}; + const forkConfig = TEST_ENV_CONFIG.fork || {}; const unlockedAccounts = forkConfig.unlockedAccounts; return web3Factory.getRpcProvider({ ...providerConfigs, @@ -172,6 +184,11 @@ export class LiveBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvi return LiveBlockchainTestsEnvironmentSingleton._instance; } + // Reset the singleton. + public static reset(): void { + LiveBlockchainTestsEnvironmentSingleton._instance = undefined; + } + protected static _createWeb3Provider(rpcHost: string): Web3ProviderEngine { const providerEngine = new Web3ProviderEngine(); providerEngine.addProvider(new RPCSubprovider(rpcHost)); @@ -223,7 +240,16 @@ export const blockchainTests: BlockchainContextDefinition = _.assign( return defineBlockchainSuite(StandardBlockchainTestsEnvironmentSingleton, description, callback, describe); }, { - config: {}, + configure(config?: Partial): void { + // Update the global config and reset all environment singletons. + TEST_ENV_CONFIG = { + ...TEST_ENV_CONFIG, + ...config, + }; + ForkedBlockchainTestsEnvironmentSingleton.reset(); + StandardBlockchainTestsEnvironmentSingleton.reset(); + LiveBlockchainTestsEnvironmentSingleton.reset(); + }, only(description: string, callback: BlockchainSuiteCallback): ISuite { return defineBlockchainSuite( StandardBlockchainTestsEnvironmentSingleton, From 809885afd0186ad27fd00bd1db9847cfe1f0f83d Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Thu, 6 Feb 2020 11:36:37 -0500 Subject: [PATCH 11/18] `@0x/contracts-asset-proxy`: Add `getMarketMarginPremium()` to `IDydx`. `@0x/contracts-dev-utils`: Handle dydx market premiums. --- .../asset-proxy/contracts/src/interfaces/IDydx.sol | 9 +++++++++ .../asset-proxy/contracts/test/TestDydxBridge.sol | 7 +++++++ contracts/dev-utils/contracts/src/D18.sol | 10 ++++++++++ contracts/dev-utils/contracts/src/LibDydxBalance.sol | 10 ++++++++++ contracts/dev-utils/contracts/test/TestDydx.sol | 9 +++++++++ 5 files changed, 45 insertions(+) diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol index 8b353ad6ac..1ec67a2dfe 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydx.sol @@ -166,6 +166,15 @@ interface IDydx { view returns (Price memory price); + /// @dev Get the margin premium for a market. A margin premium makes it so that any positions that + /// include the market require a higher collateralization to avoid being liquidated. + /// @param marketId The market to query + /// @return premium The market's margin premium + function getMarketMarginPremium(uint256 marketId) + external + view + returns (D256 memory premium); + /// @dev Get the total supplied and total borrowed values of an account adjusted by the marginPremium /// of each market. Supplied values are divided by (1 + marginPremium) for each market and /// borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these diff --git a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol index ce3f4f8b78..d23425b865 100644 --- a/contracts/asset-proxy/contracts/test/TestDydxBridge.sol +++ b/contracts/asset-proxy/contracts/test/TestDydxBridge.sol @@ -210,6 +210,13 @@ contract TestDydxBridge is returns (Price memory price) {} + /// @dev Unsused + function getMarketMarginPremium(uint256 marketId) + external + view + returns (IDydx.D256 memory premium) + {} + /// @dev Unused. function getAdjustedAccountValues( AccountInfo calldata account diff --git a/contracts/dev-utils/contracts/src/D18.sol b/contracts/dev-utils/contracts/src/D18.sol index f491976186..9334f3057e 100644 --- a/contracts/dev-utils/contracts/src/D18.sol +++ b/contracts/dev-utils/contracts/src/D18.sol @@ -57,6 +57,16 @@ library D18 { r = _add(int256(a), b); } + /// @dev Add two decimals. + function add(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(a, int256(b)); + } + /// @dev Add two decimals. function add(uint256 a, uint256 b) internal diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index 20095ef30e..822bd20bf3 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -339,6 +339,16 @@ library LibDydxBalance { } // The action value is the action rate times the price. value = D18.mul(price, value); + // Scale by the market premium. + int256 marketPremium = D18.add( + D18.one(), + info.dydx.getMarketMarginPremium(action.marketId).value + ); + if (action.actionType == IDydxBridge.BridgeActionType.Deposit) { + value = D18.div(value, marketPremium); + } else { + value = D18.mul(value, marketPremium); + } } /// @dev Returns the conversion rate for an action, expressed as units diff --git a/contracts/dev-utils/contracts/test/TestDydx.sol b/contracts/dev-utils/contracts/test/TestDydx.sol index be251af314..c2e771ca47 100644 --- a/contracts/dev-utils/contracts/test/TestDydx.sol +++ b/contracts/dev-utils/contracts/test/TestDydx.sol @@ -129,6 +129,15 @@ contract TestDydx { } } + function getMarketMarginPremium(uint256) + external + view + returns (IDydx.D256 memory premium) + { + // Return 0. + return premium; + } + function getMarketPrice( uint256 marketId ) From a425c7e26000e813609d9067114531154a1859be Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 7 Feb 2020 23:36:48 -0500 Subject: [PATCH 12/18] Regenerate artifacts and wrappers --- .../artifacts/DevUtils.json | 16 ++++-- .../src/generated-wrappers/dev_utils.ts | 53 +++++++++++++++++-- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/contract-artifacts/artifacts/DevUtils.json b/packages/contract-artifacts/artifacts/DevUtils.json index 78a392eb0f..3c07106ee5 100644 --- a/packages/contract-artifacts/artifacts/DevUtils.json +++ b/packages/contract-artifacts/artifacts/DevUtils.json @@ -6,7 +6,8 @@ { "inputs": [ { "internalType": "address", "name": "exchange_", "type": "address" }, - { "internalType": "address", "name": "chaiBridge_", "type": "address" } + { "internalType": "address", "name": "chaiBridge_", "type": "address" }, + { "internalType": "address", "name": "dydxBridge_", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", @@ -154,6 +155,15 @@ "stateMutability": "pure", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "dydxBridgeAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -828,10 +838,10 @@ }, "evm": { "bytecode": { - "object": "0x60806040523480156200001157600080fd5b50604051620051b9380380620051b98339810160408190526200003491620003e5565b600080546001600160a01b03199081166001600160a01b0385811691821784556005805490931690851617909155604051630c0e082160e31b8152829185918591906360704108906200009390630f47261b60e41b9060040162000423565b60206040518083038186803b158015620000ac57600080fd5b505afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620000e79190810190620003b5565b600180546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b815290831690636070410890620001319063012b8bc960e11b9060040162000423565b60206040518083038186803b1580156200014a57600080fd5b505afa1580156200015f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001859190810190620003b5565b600280546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b815290831690636070410890620001cf9063a7cb5fb760e01b9060040162000423565b60206040518083038186803b158015620001e857600080fd5b505afa158015620001fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002239190810190620003b5565b600380546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b8152908316906360704108906200026d9063619ce88560e11b9060040162000423565b60206040518083038186803b1580156200028657600080fd5b505afa1580156200029b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002c19190810190620003b5565b600480546001600160a01b0319166001600160a01b03928316179055600092508316159050620002f25781620002f4565b305b90506200034f6040518060400160405280600b81526020016a0c1e08141c9bdd1bd8dbdb60aa1b815250604051806040016040528060058152602001640332e302e360dc1b81525085846200035e60201b620028c21760201c565b60065550620004519350505050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b600060208284031215620003c7578081fd5b81516001600160a01b0381168114620003de578182fd5b9392505050565b60008060408385031215620003f8578081fd5b8251620004058162000438565b6020840151909250620004188162000438565b809150509250929050565b6001600160e01b031991909116815260200190565b6001600160a01b03811681146200044e57600080fd5b50565b614d5880620004616000396000f3fe608060405234801561001057600080fd5b506004361061025c5760003560e01c8063a5cd62ba11610145578063d3637905116100bd578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105b5578063ef3bb097146105bd578063ff84e7cc146105c55761025c565b8063e4e6e7da14610572578063e77286eb146105935761025c565b8063d36379051461050a578063d3d862d11461051d578063d469502814610530578063e25cabf7146105505761025c565b8063c26cfecd11610114578063ca49f47c116100f9578063ca49f47c146104c1578063d001c5dc146104e4578063d186037f146104f75761025c565b8063c26cfecd146104b1578063c82037ef146104b95761025c565b8063a5cd62ba14610449578063a6627e9f14610469578063b43cffe11461047c578063bbb2dcf61461048f5761025c565b80637982653e116101d85780639baf2705116101a75780639eadc8351161018c5780639eadc835146103ff578063a070cac814610423578063a0901e51146104365761025c565b80639baf2705146103e25780639cd01605146103f75761025c565b80637982653e1461037b57806379c9c4261461039b5780637d727512146103ae5780638f4ce479146103c15761025c565b80634dfdac201161022f57806363eb39921161021457806363eb3992146103225780636f83188e14610335578063750bdb30146103585761025c565b80634dfdac20146102e2578063590aa875146103025761025c565b806304a5618a146102615780630d7b7d761461028c5780632322cf76146102ad57806346eb65cb146102cd575b600080fd5b61027461026f366004613e8f565b6105cd565b60405161028393929190614a5a565b60405180910390f35b61029f61029a366004613953565b610666565b604051610283929190614c37565b6102c06102bb366004613953565b610688565b6040516102839190614937565b6102e06102db366004613e8f565b6106b0565b005b6102f56102f036600461386e565b610734565b604051610283919061488f565b610315610310366004613852565b6107b7565b6040516102839190614aba565b610315610330366004613997565b610866565b610348610343366004613e8f565b610919565b6040516102839493929190614b4e565b61036b610366366004613e8f565b6109b9565b6040516102839493929190614978565b61038e6103893660046141da565b610a4b565b6040516102839190614b3b565b6102c06103a93660046142e2565b610afd565b6102c06103bc366004613953565b610b7e565b6103d46103cf366004613e8f565b611474565b604051610283929190614955565b6103ea611507565b60405161028391906145e2565b6103ea611516565b61041261040d366004613e8f565b611525565b6040516102839594939291906149b4565b6102c0610431366004614289565b6115ca565b6102f5610444366004613a19565b61164b565b61045c610457366004613ae7565b6116c4565b60405161028391906147a8565b6103156104773660046139ee565b61177e565b61031561048a3660046138bc565b61182e565b6104a261049d366004613e8f565b6118e4565b60405161028393929190614a85565b6102c0611974565b6103ea61197a565b6104d46104cf366004613e8f565b611989565b6040516102839493929190614a17565b6102f56104f236600461386e565b611a1b565b6102c0610505366004613953565b611a89565b61038e6105183660046141da565b612365565b61031561052b366004613bc2565b6123c7565b61054361053e366004613e8f565b61241c565b6040516102839190614940565b61056361055e366004613b6b565b6124bf565b604051610283939291906147f4565b61058561058036600461386e565b6125f7565b604051610283929190614912565b6105a66105a1366004614232565b612610565b60405161028393929190614bdb565b6103ea61287f565b6103ea61288e565b6103ea61289d565b600080600073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a856040518263ffffffff1660e01b81526004016106099190614aba565b60606040518083038186803b15801561062157600080fd5b505af4158015610635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106599190810190613de5565b9250925092509193909250565b6000806106738484610b7e565b915061067f8484611a89565b90509250929050565b60008060006106978585610666565b915091506106a582826128ac565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$dca6deb482199a44a74cce8be70bfdd753$__906346eb65cb90610700908490600401614aba565b60006040518083038186803b15801561071857600080fd5b505af415801561072c573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610766578160200160208202803883390190505b50915060005b8181146107af576107908585838151811061078357fe5b6020026020010151611a89565b83828151811061079c57fe5b602090810291909101015260010161076c565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063590aa8759061080a9085906004016145e2565b60006040518083038186803b15801561082257600080fd5b505af4158015610836573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261085e9190810190613ec2565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__906363eb3992906108bd90879087908790600401614728565b60006040518083038186803b1580156108d557600080fd5b505af41580156108e9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613ec2565b949350505050565b60608060608073__$d88c3928727cb33b5732520dfb08856c25$__636f83188e866040518263ffffffff1660e01b81526004016109569190614aba565b60006040518083038186803b15801561096e57600080fd5b505af4158015610982573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613f30565b93509350935093509193509193565b6000806000606073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016109f79190614aba565b60006040518083038186803b158015610a0f57600080fd5b505af4158015610a23573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613c69565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91639caa023b91610aad916001600160a01b03169088908890889060040161475a565b60206040518083038186803b158015610ac557600080fd5b505af4158015610ad9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109119190810190613f11565b600061091184610b796040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612919565b600080610b91838263ffffffff61292d16565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610bec576000610bd884601063ffffffff61296616565b9050610be48186612999565b92505061146d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e055760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401610c589190614aba565b60606040518083038186803b158015610c7057600080fd5b505af4158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ca89190810190613de5565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610ce4908490602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d5f91906145c6565b600060405180830381855afa9150503d8060008114610d9a576040519150601f19603f3d011682016040523d82523d6000602084013e610d9f565b606091505b50915091506000828015610db4575081516020145b610dbf576000610dd0565b610dd082600c63ffffffff61296616565b9050896001600160a01b0316816001600160a01b031614610df2576000610df5565b60015b60ff16975050505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110a157600060608073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b8152600401610e739190614aba565b60006040518083038186803b158015610e8b57600080fd5b505af4158015610e9f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ec79190810190613cd3565b5081519296509094509250905060005b81811461109757828181518110610eea57fe5b602002602001015160001415610eff5761108f565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f3357fe5b6020026020010151604051602401610f4c92919061478f565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fc791906145c6565b600060405180830381855afa9150503d8060008114611002576040519150601f19603f3d011682016040523d82523d6000602084013e611007565b606091505b5091509150600082801561101c575081516020145b611027576000611038565b61103882600063ffffffff612a9116565b9050600087868151811061104857fe5b6020026020010151828161105857fe5b049050806110745760009b5050505050505050505050506106aa565b8b81108061108057508b155b1561108957809b505b50505050505b600101610ed7565b505050505061146d565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111d4576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061111090869060009081908190602401614acd565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b0316906111779084906145c6565b600060405180830381855afa9150503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b50509050806111c75760006111cb565b6000195b9350505061146d565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156112fd5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016112409190614aba565b60006040518083038186803b15801561125857600080fd5b505af415801561126c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112949190810190613c69565b5092509250506112a2612a9d565b6001600160a01b0316826001600160a01b03161480156112cf57506005546001600160a01b038281169116145b156112f65760006112e76112e1612ab5565b88612999565b90506112f281612acd565b9450505b505061146d565b6001600160e01b031981167f94cfcdd700000000000000000000000000000000000000000000000000000000141561146d5760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b81526004016113699190614aba565b60006040518083038186803b15801561138157600080fd5b505af4158015611395573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113bd9190810190613e25565b80519194509250905060005b818114611468578381815181106113dc57fe5b6020026020010151600014156113f157611460565b60006114108985848151811061140357fe5b6020026020010151610b7e565b9050600085838151811061142057fe5b6020026020010151828161143057fe5b049050806114485760009750505050505050506106aa565b87811080611454575087155b1561145d578097505b50505b6001016113c9565b505050505b5092915050565b60008073__$dca6deb482199a44a74cce8be70bfdd753$__638f4ce479846040518263ffffffff1660e01b81526004016114ae9190614aba565b604080518083038186803b1580156114c557600080fd5b505af41580156114d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114fd9190810190613c32565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b81526004016115649190614aba565b60006040518083038186803b15801561157c57600080fd5b505af4158015611590573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b89190810190613cd3565b939a9299509097509550909350915050565b6000610911846116466040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612ccb565b6060808251604051908082528060200260200182016040528015611679578160200160208202803883390190505b50905060005b8351811461146d5783818151811061169357fe5b60200260200101516001600160a01b0316318282815181106116b157fe5b602090810291909101015260010161167f565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$0c5fffa094d709a948ceda4c4b1013b697$__916302cffc459161172a916001600160a01b0390911690889088908890600401614610565b60006040518083038186803b15801561174257600080fd5b505af4158015611756573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613a4c565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063a6627e9f906117d3908690869060040161478f565b60006040518083038186803b1580156117eb57600080fd5b505af41580156117ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118279190810190613ec2565b9392505050565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063b43cffe1906118879088908890889088906004016146d6565b60006040518083038186803b15801561189f57600080fd5b505af41580156118b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118db9190810190613ec2565b95945050505050565b600060608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119209190614aba565b60006040518083038186803b15801561193857600080fd5b505af415801561194c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106599190810190613e25565b60065481565b6005546001600160a01b031681565b6000806060600073__$dca6deb482199a44a74cce8be70bfdd753$__63ca49f47c866040518263ffffffff1660e01b81526004016119c79190614aba565b60006040518083038186803b1580156119df57600080fd5b505af41580156119f3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613d7d565b606060008251905080604051908082528060200260200182016040528015611a4d578160200160208202803883390190505b50915060005b8181146107af57611a6a8585838151811061140357fe5b838281518110611a7657fe5b6020908102919091010152600101611a53565b600080611a9c838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c075760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b0a9190614aba565b60006040518083038186803b158015611b2257600080fd5b505af4158015611b36573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b5e9190810190613e25565b80519194509250905060005b818114611bfc57838181518110611b7d57fe5b602002602001015160001415611b9257611bf4565b6000611ba48985848151811061078357fe5b90506000858381518110611bb457fe5b60200260200101518281611bc457fe5b04905080611bdc5760009750505050505050506106aa565b87811080611be8575087155b15611bf1578097505b50505b600101611b6a565b506106aa9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c68576000611c4c84601063ffffffff61296616565b600154909150610be490829087906001600160a01b0316612cda565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fb85760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401611cd49190614aba565b60606040518083038186803b158015611cec57600080fd5b505af4158015611d00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d249190810190613de5565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611d70918a916001600160a01b03909116906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611deb91906145c6565b600060405180830381855afa9150503d8060008114611e26576040519150601f19603f3d011682016040523d82523d6000602084013e611e2b565b606091505b5091509150811580611e3f57508051602014155b80611e5b5750611e5681600063ffffffff612a9116565b600114155b15611fa9576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611e96908790602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f0d91906145c6565b600060405180830381855afa9150503d8060008114611f48576040519150601f19603f3d011682016040523d82523d6000602084013e611f4d565b606091505b509093509150828015611f61575081516020145b8015611f9057506002546001600160a01b0316611f8583600c63ffffffff61296616565b6001600160a01b0316145b611f9b576000611f9e565b60015b60ff16975050611097565b6000199650505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156121df576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__90639eadc8359061203d908790600401614aba565b60006040518083038186803b15801561205557600080fd5b505af4158015612069573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120919190810190613cd3565b5050600354604051929450606093507fe985e9c500000000000000000000000000000000000000000000000000000000926120db925089916001600160a01b0316906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b03168360405161215691906145c6565b600060405180830381855afa9150503d8060008114612191576040519150601f19603f3d011682016040523d82523d6000602084013e612196565b606091505b50915091508180156121a9575080516020145b80156121c557506121c181600063ffffffff612a9116565b6001145b6121d05760006121d4565b6000195b95505050505061146d565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561221b57600019915061146d565b6001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561146d5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016122879190614aba565b60006040518083038186803b15801561229f57600080fd5b505af41580156122b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122db9190810190613c69565b5092509250506122e9612a9d565b6001600160a01b0316826001600160a01b031614801561231657506005546001600160a01b038281169116145b1561235c57600061233b612328612ab5565b60055489906001600160a01b0316612cda565b905060001981146123545761234f81612acd565b612358565b6000195b9450505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91638dccde0491610aad916001600160a01b03169088908890889060040161475a565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063d3d862d1906117d390869086906004016148a2565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__9063d46950289061246f908590600401614aba565b60206040518083038186803b15801561248757600080fd5b505af415801561249b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061085e9190810190613c17565b60608060606000855190508060405190808252806020026020018201604052801561250457816020015b6124f16133cd565b8152602001906001900390816124e95790505b50935080604051908082528060200260200182016040528015612531578160200160208202803883390190505b5092508060405190808252806020026020018201604052801561255e578160200160208202803883390190505b50915060005b8181146125ee5761259b87828151811061257a57fe5b602002602001015187838151811061258e57fe5b6020026020010151612610565b87518890859081106125a957fe5b602002602001018785815181106125bc57fe5b602002602001018786815181106125cf57fe5b9315156020948502919091019093019290925291905252600101612564565b50509250925092565b6060806126048484611a1b565b915061067f8484610734565b6126186133cd565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b990612662908890600401614bff565b60606040518083038186803b15801561267a57600080fd5b505afa15801561268e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126b29190810190614192565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127049089908990600401614c12565b60206040518083038186803b15801561271c57600080fd5b505afa158015612730573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127549190810190613bf7565b9150600061276187612de4565b90506000612782886101800151896101400151612e0090919063ffffffff16565b156127b7576127b0826127a68a60c001518b60800151612e2590919063ffffffff16565b8a60a00151612e41565b9050612824565b60c08801516127d3576127b08289608001518a60a00151612e41565b60006127e4848a6101800151610688565b905060006127fb848b608001518c60a00151612e41565b90506000612812838c60c001518d60a00151612e41565b905061281e82826128ac565b93505050505b61284861284287604001518a60a00151612e6390919063ffffffff16565b826128ac565b945061285388612e82565b61285c57600094505b60038651600681111561286b57fe5b1461287557600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b60008183106128bb5781611827565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b60006118278261292885612ee9565b612f64565b600081600401835110156129535761295361294e6003855185600401612f9e565b61300d565b5001602001516001600160e01b03191690565b600081601401835110156129875761298761294e6004855185601401612f9e565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a0823100000000000000000000000000000000000000000000000000000000906129df9087906024016145e2565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612a3291906145c6565b600060405180830381855afa9150503d8060008114612a6d576040519150601f19603f3d011682016040523d82523d6000602084013e612a72565b606091505b5091509150818015612a85575080516020145b156107af576106a58160005b60006118278383613015565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612ad8612ab5565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b1257600080fd5b505af1158015612b26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b4a9190810190613ef5565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b8957600080fd5b505af1158015612b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bc1919081019061439a565b4211612c3f57816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c3a919081019061439a565b612cb2565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c7a57600080fd5b505af1158015612c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cb2919081019061439a565b9050610911816b033b2e3c9fd0803ce800000086612e41565b6000611827826129288561303f565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612d2290889088906024016145f6565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612d7591906145c6565b600060405180830381855afa9150503d8060008114612db0576040519150601f19603f3d011682016040523d82523d6000602084013e612db5565b606091505b5091509150818015612dc8575080516020145b15612ddb57612dd8816000612a91565b92505b50509392505050565b6000806000612df2846130f8565b9150915061091182826128ac565b6000815183511480156118275750508051602091820120825192909101919091201490565b6000828201838110156118275761182761294e6000868661313b565b600061091183612e57868563ffffffff61315a16565b9063ffffffff61318b16565b600082821115612e7c57612e7c61294e6002858561313b565b50900390565b6000612e928261014001516131b5565b8015612eb3575060c08201511580612eb35750612eb38261018001516131b5565b8015612ec85750612ec88261016001516131b5565b801561085e575060e0820151158061085e575061085e826101a001516131b5565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b848484604051602401612fbd93929190614b2d565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156130365761303661294e6005855185602001612f9e565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561308b57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b600080600483610140015151101561311557506000905080611502565b6131288360000151846101400151610b7e565b6114fd8460000151856101400151611a89565b606063e946c1bb60e01b848484604051602401612fbd93929190614b0b565b600082613169575060006106aa565b8282028284828161317657fe5b04146118275761182761294e6001868661313b565b6000816131a1576131a161294e6003858561313b565b60008284816131ac57fe5b04949350505050565b600060208251816131c257fe5b066004146131d257506000610861565b60006131e4838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613221576001915050610861565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063bbb2dcf690613274908790600401614aba565b60006040518083038186803b15801561328c57600080fd5b505af41580156132a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132c89190810190613e25565b80519093509150600090505b81811461335e57600061330460008584815181106132ee57fe5b602002602001015161292d90919063ffffffff16565b90506001600160e01b031981167f0257179200000000000000000000000000000000000000000000000000000000141561335557613342848361336a565b1561335557600095505050505050610861565b506001016132d4565b50600195945050505050565b8151600090600183015b818110156107af576133b585828151811061338b57fe5b602002602001015186868151811061339f57fe5b6020026020010151612e0090919063ffffffff16565b156133c5576001925050506106aa565b600101613374565b6040805160608101909152806000815260006020820181905260409091015290565b80356106aa81614cea565b80516106aa81614cea565b600082601f830112613415578081fd5b813561342861342382614c6c565b614c45565b81815291506020808301908481018184028601820187101561344957600080fd5b60005b8481101561146857813561345f81614cea565b8452928201929082019060010161344c565b600082601f830112613481578081fd5b813561348f61342382614c6c565b8181529150602080830190840160005b838110156134cc576134b78760208435890101613660565b8352602092830192919091019060010161349f565b5050505092915050565b600082601f8301126134e6578081fd5b81516134f461342382614c6c565b8181529150602080830190840160005b838110156134cc5761351c87602084518901016136ae565b83526020928301929190910190600101613504565b600082601f830112613541578081fd5b813561354f61342382614c6c565b8181529150602080830190840160005b838110156134cc576135778760208435890101613703565b8352602092830192919091019060010161355f565b600082601f83011261359c578081fd5b81356135aa61342382614c6c565b8181529150602080830190848101818402860182018710156135cb57600080fd5b60005b84811015611468578135845292820192908201906001016135ce565b600082601f8301126135fa578081fd5b815161360861342382614c6c565b81815291506020808301908481018184028601820187101561362957600080fd5b60005b848110156114685781518452928201929082019060010161362c565b80516001600160e01b0319811681146106aa57600080fd5b600082601f830112613670578081fd5b813561367e61342382614c8c565b915080825283602082850101111561369557600080fd5b8060208401602084013760009082016020015292915050565b600082601f8301126136be578081fd5b81516136cc61342382614c8c565b91508082528360208285010111156136e357600080fd5b61146d816020840160208601614cb0565b8051600581106106aa57600080fd5b60006101c0808385031215613716578182fd5b61371f81614c45565b91505061372c83836133ef565b815261373b83602084016133ef565b602082015261374d83604084016133ef565b604082015261375f83606084016133ef565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156137c157600080fd5b6137cd86838701613660565b838501526101609250828501359150808211156137e957600080fd5b6137f586838701613660565b8385015261018092508285013591508082111561381157600080fd5b61381d86838701613660565b838501526101a092508285013591508082111561383957600080fd5b5061384685828601613660565b82840152505092915050565b600060208284031215613863578081fd5b813561182781614cea565b60008060408385031215613880578081fd5b823561388b81614cea565b9150602083013567ffffffffffffffff8111156138a6578182fd5b6138b285828601613471565b9150509250929050565b600080600080608085870312156138d1578182fd5b84356138dc81614cea565b9350602085013567ffffffffffffffff808211156138f8578384fd5b6139048883890161358c565b94506040870135915080821115613919578384fd5b6139258883890161358c565b9350606087013591508082111561393a578283fd5b5061394787828801613660565b91505092959194509250565b60008060408385031215613965578182fd5b823561397081614cea565b9150602083013567ffffffffffffffff81111561398b578182fd5b6138b285828601613660565b6000806000606084860312156139ab578081fd5b83356139b681614cea565b9250602084013567ffffffffffffffff8111156139d1578182fd5b6139dd86828701613660565b925050604084013590509250925092565b60008060408385031215613a00578182fd5b8235613a0b81614cea565b946020939093013593505050565b600060208284031215613a2a578081fd5b813567ffffffffffffffff811115613a40578182fd5b61091184828501613405565b60006020808385031215613a5e578182fd5b825167ffffffffffffffff811115613a74578283fd5b80840185601f820112613a85578384fd5b80519150613a9561342383614c6c565b8281528381019082850185850284018601891015613ab1578687fd5b8693505b84841015613adb57613ac789826136f4565b835260019390930192918501918501613ab5565b50979650505050505050565b600080600060608486031215613afb578081fd5b833567ffffffffffffffff80821115613b12578283fd5b613b1e87838801613531565b94506020860135915080821115613b33578283fd5b613b3f87838801613405565b93506040860135915080821115613b54578283fd5b50613b618682870161358c565b9150509250925092565b60008060408385031215613b7d578182fd5b823567ffffffffffffffff80821115613b94578384fd5b613ba086838701613531565b93506020850135915080821115613bb5578283fd5b506138b285828601613471565b60008060408385031215613bd4578182fd5b823567ffffffffffffffff80821115613beb578384fd5b613ba08683870161358c565b600060208284031215613c08578081fd5b81518015158114611827578182fd5b600060208284031215613c28578081fd5b6118278383613648565b60008060408385031215613c44578182fd5b613c4e8484613648565b91506020830151613c5e81614cea565b809150509250929050565b60008060008060808587031215613c7e578182fd5b8451613c8981614cff565b6020860151909450613c9a81614cea565b6040860151909350613cab81614cea565b606086015190925067ffffffffffffffff811115613cc7578182fd5b613947878288016136ae565b600080600080600060a08688031215613cea578283fd5b613cf48787613648565b94506020860151613d0481614cea565b604087015190945067ffffffffffffffff80821115613d21578485fd5b613d2d89838a016135ea565b94506060880151915080821115613d42578283fd5b613d4e89838a016135ea565b93506080880151915080821115613d63578283fd5b50613d70888289016136ae565b9150509295509295909350565b60008060008060808587031215613d92578182fd5b613d9c8686613648565b93506020850151613dac81614cea565b604086015190935067ffffffffffffffff811115613dc8578283fd5b613dd4878288016136ae565b606096909601519497939650505050565b600080600060608486031215613df9578081fd5b613e038585613648565b92506020840151613e1381614cea565b80925050604084015190509250925092565b600080600060608486031215613e39578081fd5b8351613e4481614cff565b602085015190935067ffffffffffffffff80821115613e61578283fd5b613e6d878388016135ea565b93506040860151915080821115613e82578283fd5b50613b61868287016134d6565b600060208284031215613ea0578081fd5b813567ffffffffffffffff811115613eb6578182fd5b61091184828501613660565b600060208284031215613ed3578081fd5b815167ffffffffffffffff811115613ee9578182fd5b610911848285016136ae565b600060208284031215613f06578081fd5b815161182781614cea565b600060208284031215613f22578081fd5b815160058110611827578182fd5b60008060008060808587031215613f45578182fd5b845167ffffffffffffffff811115613f5b578283fd5b613f67878288016136ae565b945050602085015167ffffffffffffffff811115613f83578283fd5b86601f8288010112613f93578283fd5b80860151613fa361342382614c6c565b8181526020808201919089850101865b8481101561413c578151868c01016101c0601f19828f03011215613fd5578889fd5b613fe06101c0614c45565b613fed8e602084016133fa565b8152613ffc8e604084016133fa565b602082015261400e8e606084016133fa565b60408201526140208e608084016133fa565b606082015260a0820151608082015260c082015160a082015260e082015160c082015261010082015160e082015261012082015161010082015261014082015161012082015261016082015167ffffffffffffffff811115614080578a8bfd5b61408f8f6020838601016136ae565b6101408301525061018082015167ffffffffffffffff8111156140b0578a8bfd5b6140bf8f6020838601016136ae565b610160830152506101a082015167ffffffffffffffff8111156140e0578a8bfd5b6140ef8f6020838601016136ae565b610180830152506101c082015167ffffffffffffffff811115614110578a8bfd5b61411f8f6020838601016136ae565b6101a0830152508552506020938401939190910190600101613fb3565b505080965050505050604085015167ffffffffffffffff81111561415e578283fd5b61416a878288016135ea565b925050606085015167ffffffffffffffff811115614186578182fd5b613947878288016134d6565b6000606082840312156141a3578081fd5b6141ad6060614c45565b8251600781106141bb578283fd5b8152602083810151908201526040928301519281019290925250919050565b6000806000606084860312156141ee578081fd5b833567ffffffffffffffff811115614204578182fd5b61421086828701613703565b935050602084013561422181614cea565b929592945050506040919091013590565b60008060408385031215614244578182fd5b823567ffffffffffffffff8082111561425b578384fd5b61426786838701613703565b9350602085013591508082111561427c578283fd5b506138b285828601613660565b60008060006060848603121561429d578081fd5b833567ffffffffffffffff8111156142b3578182fd5b6142bf86828701613703565b9350506020840135915060408401356142d781614cea565b809150509250925092565b6000806000606084860312156142f6578081fd5b833567ffffffffffffffff8082111561430d578283fd5b81860160a0818903121561431f578384fd5b61432960a0614c45565b925080358352602081013560208401526040810135604084015261435088606083016133ef565b6060840152608081013582811115614366578485fd5b61437289828401613660565b60808501525091945050506020840135915061439185604086016133ef565b90509250925092565b6000602082840312156143ab578081fd5b5051919050565b6001600160a01b0316815260200190565b60006143cf83836144a2565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b8481101561442f578284038852614419848351614476565b6020988901989094509190910190600101614401565b50919695505050505050565b6000815180845260208401935060208301825b8281101561446c57815186526020958601959091019060010161444e565b5093949350505050565b6000815180845261448e816020860160208601614cb0565b601f01601f19169290920160200192915050565b8051600781106144ae57fe5b825260208181015190830152604090810151910152565b60006101c06144d58484516143d7565b60208301516144e760208601826143d7565b5060408301516144fa60408601826143d7565b50606083015161450d60608601826143d7565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010080840151818601525061012080840151818601525061014080840151828287015261456683870182614476565b915050610160915081840151858203838701526145838282614476565b92505050610180808401518583038287015261459f8382614476565b9150506101a0915081840151858203838701526145bc8282614476565b9695505050505050565b600082516145d8818460208701614cb0565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516146378185614937565b91508193508281028201838a01865b8381101561467057868303855261465e8383516144c5565b94860194925090850190600101614646565b505086810360408801528094508851925061468b8382614937565b94505050818701845b828110156146b5576146a78583516143b2565b945090830190600101614694565b5050505082810360608401526146cb818561443b565b979650505050505050565b60006001600160a01b0386168252608060208301526146f8608083018661443b565b828103604084015261470a818661443b565b838103606085015261471c8186614476565b98975050505050505050565b60006001600160a01b03851682526060602083015261474a6060830185614476565b9050826040830152949350505050565b60006001600160a01b0380871683526080602084015261477d60808401876144c5565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b818110156147e95783516147d581614ce0565b8352602093840193909201916001016147c2565b509095945050505050565b6000606082016060835280865161480b8184614937565b915060209250828801845b82811015614837576148298483516143c3565b935090840190600101614816565b5050508381038285015261484b818761443b565b84810360408601528551808252908301915082860190845b81811015614881578251151584529284019291840191600101614863565b509198975050505050505050565b600060208252611827602083018461443b565b6000604082526148b5604083018561443b565b602083820381850152818551808452828401915082838202850101838801865b8381101561490357601f198784030185526148f1838351614476565b948601949250908501906001016148d5565b50909998505050505050505050565b600060408252614925604083018561443b565b82810360208401526118db818561443b565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b038086166020840152808516604084015250608060608301526145bc6080830184614476565b60006001600160e01b0319871682526001600160a01b038616602083015260a060408301526149e660a083018661443b565b82810360608401526149f8818661443b565b8381036080850152614a0a8186614476565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614a496080830185614476565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614aa8606083018561443b565b82810360408401526145bc81856143e4565b6000602082526118276020830184614476565b600060808252614ae06080830187614476565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614b1957fe5b938152602081019290925260409091015290565b6060810160088510614b1957fe5b60208101614b4883614ce0565b91905290565b600060808252614b616080830187614476565b602083820381850152818751808452828401915082838202850101838a01865b83811015614baf57601f19878403018552614b9d8383516144c5565b94860194925090850190600101614b81565b50508681036040880152614bc3818a61443b565b94505050505082810360608401526146cb81856143e4565b60a08101614be982866144a2565b8360608301528215156080830152949350505050565b60006020825261182760208301846144c5565b600060408252614c2560408301856144c5565b82810360208401526118db8185614476565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614c6457600080fd5b604052919050565b600067ffffffffffffffff821115614c82578081fd5b5060209081020190565b600067ffffffffffffffff821115614ca2578081fd5b50601f01601f191660200190565b60005b83811015614ccb578181015183820152602001614cb3565b83811115614cda576000848401525b50505050565b6005811061073157fe5b6001600160a01b038116811461073157600080fd5b6001600160e01b03198116811461073157600080fdfea365627a7a72315820dc2fa6b39099cbef3d852e9f64db6bbaec7f3795508fc6ad2c43d1a719227f996c6578706572696d656e74616cf564736f6c63430005100040" + "object": "0x60806040523480156200001157600080fd5b5060405162005498380380620054988339810160408190526200003491620003f5565b600080546001600160a01b03199081166001600160a01b0386811691821784556005805484168783161790556006805490931690851617909155604051630c0e082160e31b81528291869186918691636070410890620000a090630f47261b60e41b9060040162000448565b60206040518083038186803b158015620000b957600080fd5b505afa158015620000ce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620000f49190810190620003c5565b600180546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b8152908416906360704108906200013e9063012b8bc960e11b9060040162000448565b60206040518083038186803b1580156200015757600080fd5b505afa1580156200016c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001929190810190620003c5565b600280546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b815290841690636070410890620001dc9063a7cb5fb760e01b9060040162000448565b60206040518083038186803b158015620001f557600080fd5b505afa1580156200020a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002309190810190620003c5565b600380546001600160a01b0319166001600160a01b03928316179055604051630c0e082160e31b8152908416906360704108906200027a9063619ce88560e11b9060040162000448565b60206040518083038186803b1580156200029357600080fd5b505afa158015620002a8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002ce9190810190620003c5565b600480546001600160a01b0319166001600160a01b03928316179055600093508416159150620003019050578162000303565b305b90506200035e6040518060400160405280600b81526020016a0c1e08141c9bdd1bd8dbdb60aa1b815250604051806040016040528060058152602001640332e302e360dc1b81525085846200036e60201b62002a151760201c565b6007555062000476945050505050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b600060208284031215620003d7578081fd5b81516001600160a01b0381168114620003ee578182fd5b9392505050565b6000806000606084860312156200040a578182fd5b835162000417816200045d565b60208501519093506200042a816200045d565b60408501519092506200043d816200045d565b809150509250925092565b6001600160e01b031991909116815260200190565b6001600160a01b03811681146200047357600080fd5b50565b61501280620004866000396000f3fe608060405234801561001057600080fd5b50600436106102775760003560e01c8063a5cd62ba11610160578063d186037f116100d8578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105d8578063ef3bb097146105e0578063ff84e7cc146105e857610277565b8063e4e6e7da14610595578063e77286eb146105b657610277565b8063d3d862d1116100bd578063d3d862d114610540578063d469502814610553578063e25cabf71461057357610277565b8063d186037f1461051a578063d36379051461052d57610277565b8063bbb2dcf61161012f578063c82037ef11610114578063c82037ef146104dc578063ca49f47c146104e4578063d001c5dc1461050757610277565b8063bbb2dcf6146104b2578063c26cfecd146104d457610277565b8063a5cd62ba14610464578063a6627e9f14610484578063a7530f1214610497578063b43cffe11461049f57610277565b80637982653e116101f35780639baf2705116101c25780639eadc835116101a75780639eadc8351461041a578063a070cac81461043e578063a0901e511461045157610277565b80639baf2705146103fd5780639cd016051461041257610277565b80637982653e1461039657806379c9c426146103b65780637d727512146103c95780638f4ce479146103dc57610277565b80634dfdac201161024a57806363eb39921161022f57806363eb39921461033d5780636f83188e14610350578063750bdb301461037357610277565b80634dfdac20146102fd578063590aa8751461031d57610277565b806304a5618a1461027c5780630d7b7d76146102a75780632322cf76146102c857806346eb65cb146102e8575b600080fd5b61028f61028a366004614296565b6105f0565b60405161029e93929190614ce9565b60405180910390f35b6102ba6102b5366004613d5a565b610689565b60405161029e929190614ef1565b6102db6102d6366004613d5a565b6106ab565b60405161029e9190614bc6565b6102fb6102f6366004614296565b6106d3565b005b61031061030b366004613c75565b610757565b60405161029e9190614b1e565b61033061032b366004613c59565b6107da565b60405161029e9190614d49565b61033061034b366004613d9e565b610889565b61036361035e366004614296565b61093c565b60405161029e9493929190614ddd565b610386610381366004614296565b6109dc565b60405161029e9493929190614c07565b6103a96103a4366004614468565b610a6e565b60405161029e9190614dca565b6102db6103c4366004614570565b610b20565b6102db6103d7366004613d5a565b610ba1565b6103ef6103ea366004614296565b611497565b60405161029e929190614be4565b61040561152a565b60405161029e9190614871565b610405611539565b61042d610428366004614296565b611548565b60405161029e959493929190614c43565b6102db61044c366004614517565b6115ed565b61031061045f366004613e20565b61166e565b610477610472366004613eee565b6116e7565b60405161029e9190614a37565b610330610492366004613df5565b6117a1565b610405611851565b6103306104ad366004613cc3565b611860565b6104c56104c0366004614296565b611916565b60405161029e93929190614d14565b6102db6119a6565b6104056119ac565b6104f76104f2366004614296565b6119bb565b60405161029e9493929190614ca6565b610310610515366004613c75565b611a4d565b6102db610528366004613d5a565b611abb565b6103a961053b366004614468565b6123b7565b61033061054e366004613fc9565b612419565b610566610561366004614296565b61246e565b60405161029e9190614bcf565b610586610581366004613f72565b612511565b60405161029e93929190614a83565b6105a86105a3366004613c75565b612649565b60405161029e929190614ba1565b6105c96105c43660046144c0565b612662565b60405161029e93929190614e6a565b6104056129d2565b6104056129e1565b6104056129f0565b600080600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a856040518263ffffffff1660e01b815260040161062c9190614d49565b60606040518083038186803b15801561064457600080fd5b505af4158015610658573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061067c91908101906141ec565b9250925092509193909250565b6000806106968484610ba1565b91506106a28484611abb565b90509250929050565b60008060006106ba8585610689565b915091506106c882826129ff565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$d8b635de2bf4a097b4e18b67d0fb68e779$__906346eb65cb90610723908490600401614d49565b60006040518083038186803b15801561073b57600080fd5b505af415801561074f573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610789578160200160208202803883390190505b50915060005b8181146107d2576107b3858583815181106107a657fe5b6020026020010151611abb565b8382815181106107bf57fe5b602090810291909101015260010161078f565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063590aa8759061082d908590600401614871565b60006040518083038186803b15801561084557600080fd5b505af4158015610859573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261088191908101906142c9565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__906363eb3992906108e0908790879087906004016149b7565b60006040518083038186803b1580156108f857600080fd5b505af415801561090c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261093491908101906142c9565b949350505050565b60608060608073__$fddee3b26a806429350031cf3481976d2e$__636f83188e866040518263ffffffff1660e01b81526004016109799190614d49565b60006040518083038186803b15801561099157600080fd5b505af41580156109a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614333565b93509350935093509193509193565b6000806000606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b8152600401610a1a9190614d49565b60006040518083038186803b158015610a3257600080fd5b505af4158015610a46573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614070565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91639caa023b91610ad0916001600160a01b0316908890889088906004016149e9565b60206040518083038186803b158015610ae857600080fd5b505af4158015610afc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109349190810190614318565b600061093484610b9c6040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612a6c565b600080610bb4838263ffffffff612a8016565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610c0f576000610bfb84601063ffffffff612ab916565b9050610c078186612aec565b925050611490565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e285760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401610c7b9190614d49565b60606040518083038186803b158015610c9357600080fd5b505af4158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccb91908101906141ec565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610d07908490602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d829190614855565b600060405180830381855afa9150503d8060008114610dbd576040519150601f19603f3d011682016040523d82523d6000602084013e610dc2565b606091505b50915091506000828015610dd7575081516020145b610de2576000610df3565b610df382600c63ffffffff612ab916565b9050896001600160a01b0316816001600160a01b031614610e15576000610e18565b60015b60ff169750505050505050611490565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110c457600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b8152600401610e969190614d49565b60006040518083038186803b158015610eae57600080fd5b505af4158015610ec2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610eea91908101906140da565b5081519296509094509250905060005b8181146110ba57828181518110610f0d57fe5b602002602001015160001415610f22576110b2565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f5657fe5b6020026020010151604051602401610f6f929190614a1e565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fea9190614855565b600060405180830381855afa9150503d8060008114611025576040519150601f19603f3d011682016040523d82523d6000602084013e61102a565b606091505b5091509150600082801561103f575081516020145b61104a57600061105b565b61105b82600063ffffffff612be416565b9050600087868151811061106b57fe5b6020026020010151828161107b57fe5b049050806110975760009b5050505050505050505050506106cd565b8b8110806110a357508b155b156110ac57809b505b50505050505b600101610efa565b5050505050611490565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111f7576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061113390869060009081908190602401614d5c565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b03169061119a908490614855565b600060405180830381855afa9150503d80600081146111d5576040519150601f19603f3d011682016040523d82523d6000602084013e6111da565b606091505b50509050806111ea5760006111ee565b6000195b93505050611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156113205760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016112639190614d49565b60006040518083038186803b15801561127b57600080fd5b505af415801561128f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112b79190810190614070565b5092509250506112c5612bf0565b6001600160a01b0316826001600160a01b03161480156112f257506005546001600160a01b038281169116145b1561131957600061130a611304612c08565b88612aec565b905061131581612c20565b9450505b5050611490565b6001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014156114905760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b815260040161138c9190614d49565b60006040518083038186803b1580156113a457600080fd5b505af41580156113b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113e0919081019061422c565b80519194509250905060005b81811461148b578381815181106113ff57fe5b60200260200101516000141561141457611483565b60006114338985848151811061142657fe5b6020026020010151610ba1565b9050600085838151811061144357fe5b6020026020010151828161145357fe5b0490508061146b5760009750505050505050506106cd565b87811080611477575087155b15611480578097505b50505b6001016113ec565b505050505b5092915050565b60008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__638f4ce479846040518263ffffffff1660e01b81526004016114d19190614d49565b604080518083038186803b1580156114e857600080fd5b505af41580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115209190810190614039565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b81526004016115879190614d49565b60006040518083038186803b15801561159f57600080fd5b505af41580156115b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115db91908101906140da565b939a9299509097509550909350915050565b6000610934846116696040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612e1e565b606080825160405190808252806020026020018201604052801561169c578160200160208202803883390190505b50905060005b83518114611490578381815181106116b657fe5b60200260200101516001600160a01b0316318282815181106116d457fe5b60209081029190910101526001016116a2565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$7a69f714cdde1cb6b62e3c39bc8c94deae$__916302cffc459161174d916001600160a01b039091169088908890889060040161489f565b60006040518083038186803b15801561176557600080fd5b505af4158015611779573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109349190810190613e53565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063a6627e9f906117f69086908690600401614a1e565b60006040518083038186803b15801561180e57600080fd5b505af4158015611822573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261184a91908101906142c9565b9392505050565b6006546001600160a01b031681565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063b43cffe1906118b9908890889088908890600401614965565b60006040518083038186803b1580156118d157600080fd5b505af41580156118e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261190d91908101906142c9565b95945050505050565b600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119529190614d49565b60006040518083038186803b15801561196a57600080fd5b505af415801561197e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261067c919081019061422c565b60075481565b6005546001600160a01b031681565b6000806060600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63ca49f47c866040518263ffffffff1660e01b81526004016119f99190614d49565b60006040518083038186803b158015611a1157600080fd5b505af4158015611a25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614184565b606060008251905080604051908082528060200260200182016040528015611a7f578160200160208202803883390190505b50915060005b8181146107d257611a9c8585838151811061142657fe5b838281518110611aa857fe5b6020908102919091010152600101611a85565b600080611ace838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c395760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b3c9190614d49565b60006040518083038186803b158015611b5457600080fd5b505af4158015611b68573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b90919081019061422c565b80519194509250905060005b818114611c2e57838181518110611baf57fe5b602002602001015160001415611bc457611c26565b6000611bd6898584815181106107a657fe5b90506000858381518110611be657fe5b60200260200101518281611bf657fe5b04905080611c0e5760009750505050505050506106cd565b87811080611c1a575087155b15611c23578097505b50505b600101611b9c565b506106cd9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c9a576000611c7e84601063ffffffff612ab916565b600154909150610c0790829087906001600160a01b0316612e2d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fea5760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401611d069190614d49565b60606040518083038186803b158015611d1e57600080fd5b505af4158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d5691908101906141ec565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611da2918a916001600160a01b0390911690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611e1d9190614855565b600060405180830381855afa9150503d8060008114611e58576040519150601f19603f3d011682016040523d82523d6000602084013e611e5d565b606091505b5091509150811580611e7157508051602014155b80611e8d5750611e8881600063ffffffff612be416565b600114155b15611fdb576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611ec8908790602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f3f9190614855565b600060405180830381855afa9150503d8060008114611f7a576040519150601f19603f3d011682016040523d82523d6000602084013e611f7f565b606091505b509093509150828015611f93575081516020145b8015611fc257506002546001600160a01b0316611fb783600c63ffffffff612ab916565b6001600160a01b0316145b611fcd576000611fd0565b60015b60ff169750506110ba565b60001996505050505050611490565b6001600160e01b031981167fa7cb5fb7000000000000000000000000000000000000000000000000000000001415612211576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__90639eadc8359061206f908790600401614d49565b60006040518083038186803b15801561208757600080fd5b505af415801561209b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120c391908101906140da565b5050600354604051929450606093507fe985e9c5000000000000000000000000000000000000000000000000000000009261210d925089916001600160a01b031690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b0316836040516121889190614855565b600060405180830381855afa9150503d80600081146121c3576040519150601f19603f3d011682016040523d82523d6000602084013e6121c8565b606091505b50915091508180156121db575080516020145b80156121f757506121f381600063ffffffff612be416565b6001145b612202576000612206565b6000195b955050505050611490565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561224d576000199150611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156114905760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016122b99190614d49565b60006040518083038186803b1580156122d157600080fd5b505af41580156122e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261230d9190810190614070565b50925092505061231b612bf0565b6001600160a01b0316826001600160a01b031614801561234857506005546001600160a01b038281169116145b1561239257600061236d61235a612c08565b60055489906001600160a01b0316612e2d565b905060001981146123865761238181612c20565b61238a565b6000195b9450506123ae565b6006546001600160a01b03828116911614156123ae5760001993505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91638dccde0491610ad0916001600160a01b0316908890889088906004016149e9565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d3d862d1906117f69086908690600401614b31565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d4695028906124c1908590600401614d49565b60206040518083038186803b1580156124d957600080fd5b505af41580156124ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610881919081019061401e565b60608060606000855190508060405190808252806020026020018201604052801561255657816020015b612543613691565b81526020019060019003908161253b5790505b50935080604051908082528060200260200182016040528015612583578160200160208202803883390190505b509250806040519080825280602002602001820160405280156125b0578160200160208202803883390190505b50915060005b818114612640576125ed8782815181106125cc57fe5b60200260200101518783815181106125e057fe5b6020026020010151612662565b87518890859081106125fb57fe5b6020026020010187858151811061260e57fe5b6020026020010187868151811061262157fe5b93151560209485029190910190930192909252919052526001016125b6565b50509250925092565b6060806126568484611a4d565b91506106a28484610757565b61266a613691565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b9906126b4908890600401614e8e565b60606040518083038186803b1580156126cc57600080fd5b505afa1580156126e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127049190810190614420565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127569089908990600401614ecc565b60206040518083038186803b15801561276e57600080fd5b505afa158015612782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127a69190810190613ffe565b915060006127b387612f37565b905060006127d4886101800151896101400151612f5390919063ffffffff16565b1561280957612802826127f88a60c001518b60800151612f7890919063ffffffff16565b8a60a00151612f94565b9050612876565b60c0880151612825576128028289608001518a60a00151612f94565b6000612836848a61018001516106ab565b9050600061284d848b608001518c60a00151612f94565b90506000612864838c60c001518d60a00151612f94565b905061287082826129ff565b93505050505b61289a61289487604001518a60a00151612fb690919063ffffffff16565b826129ff565b9450600460005460208a01516040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__92639caa023b92612905926001600160a01b03909216918e91908c906004016149e9565b60206040518083038186803b15801561291d57600080fd5b505af4158015612931573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129559190810190614318565b600481111561296057fe5b1461296c57600061296e565b845b945061297e886101600151612fd5565b61298757600094505b60e0880151158015906129a557506129a3886101a00151612fd5565b155b156129af57600094505b6003865160068111156129be57fe5b146129c857600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b6000818310612a0e578161184a565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b600061184a82612a7b8561318a565b613205565b60008160040183511015612aa657612aa6612aa1600385518560040161323f565b6132ae565b5001602001516001600160e01b03191690565b60008160140183511015612ada57612ada612aa1600485518560140161323f565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a082310000000000000000000000000000000000000000000000000000000090612b32908790602401614871565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612b859190614855565b600060405180830381855afa9150503d8060008114612bc0576040519150601f19603f3d011682016040523d82523d6000602084013e612bc5565b606091505b5091509150818015612bd8575080516020145b156107d2576106c88160005b600061184a83836132b6565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612c2b612c08565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c6557600080fd5b505af1158015612c79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c9d91908101906142fc565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612cdc57600080fd5b505af1158015612cf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d149190810190614629565b4211612d9257816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612d5557600080fd5b505af1158015612d69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d8d9190810190614629565b612e05565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612dcd57600080fd5b505af1158015612de1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e059190810190614629565b9050610934816b033b2e3c9fd0803ce800000086612f94565b600061184a82612a7b856132e0565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612e759088908890602401614885565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612ec89190614855565b600060405180830381855afa9150503d8060008114612f03576040519150601f19603f3d011682016040523d82523d6000602084013e612f08565b606091505b5091509150818015612f1b575080516020145b15612f2e57612f2b816000612be4565b92505b50509392505050565b6000806000612f4584613399565b9150915061093482826129ff565b60008151835114801561184a5750508051602091820120825192909101919091201490565b60008282018381101561184a5761184a612aa16000868661359c565b600061093483612faa868563ffffffff6135bb16565b9063ffffffff6135ec16565b600082821115612fcf57612fcf612aa16002858561359c565b50900390565b60006020825181612fe257fe5b06600414612ff257506000610884565b6000613004838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613041576001915050610884565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063bbb2dcf690613094908790600401614d49565b60006040518083038186803b1580156130ac57600080fd5b505af41580156130c0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130e8919081019061422c565b80519093509150600090505b81811461317e576000613124600085848151811061310e57fe5b6020026020010151612a8090919063ffffffff16565b90506001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415613175576131628483613616565b1561317557600095505050505050610884565b506001016130f4565b50600195945050505050565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b84848460405160240161325e93929190614dbc565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156132d7576132d7612aa1600585518560200161323f565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561332c57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b60008060048361014001515110156133b657506000905080611525565b6101408301516000906133cf908263ffffffff612a8016565b90506001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561356c576101408401516040517f750bdb3000000000000000000000000000000000000000000000000000000000815260009173__$d8b635de2bf4a097b4e18b67d0fb68e779$__9163750bdb309161345a91600401614d49565b60006040518083038186803b15801561347257600080fd5b505af4158015613486573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134ae9190810190614070565b506006549093506001600160a01b0380851691161415915061356a90505773__$ef9cb1cf4426222cc0af8204def2680bac$__63d12a7960866134ef613679565b6040518363ffffffff1660e01b815260040161350c929190614ea1565b60206040518083038186803b15801561352457600080fd5b505af4158015613538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061355c9190810190614629565b600019935093505050611525565b505b61357f8460000151856101400151610ba1565b6135928560000151866101400151611abb565b9250925050915091565b606063e946c1bb60e01b84848460405160240161325e93929190614d9a565b6000826135ca575060006106cd565b828202828482816135d757fe5b041461184a5761184a612aa16001868661359c565b60008161360257613602612aa16003858561359c565b600082848161360d57fe5b04949350505050565b8151600090600183015b818110156107d25761366185828151811061363757fe5b602002602001015186868151811061364b57fe5b6020026020010151612f5390919063ffffffff16565b15613671576001925050506106cd565b600101613620565b731e0447b19bb6ecfdae1e4ae1694b0c3659614e4e90565b6040805160608101909152806000815260006020820181905260409091015290565b80356106cd81614fa4565b80516106cd81614fa4565b600082601f8301126136d9578081fd5b81356136ec6136e782614f26565b614eff565b81815291506020808301908481018184028601820187101561370d57600080fd5b60005b8481101561148b57813561372381614fa4565b84529282019290820190600101613710565b600082601f830112613745578081fd5b81356137536136e782614f26565b8181529150602080830190840160005b838110156137905761377b8760208435890101613924565b83526020928301929190910190600101613763565b5050505092915050565b600082601f8301126137aa578081fd5b81516137b86136e782614f26565b8181529150602080830190840160005b83811015613790576137e08760208451890101613972565b835260209283019291909101906001016137c8565b600082601f830112613805578081fd5b81356138136136e782614f26565b8181529150602080830190840160005b838110156137905761383b87602084358901016139c7565b83526020928301929190910190600101613823565b600082601f830112613860578081fd5b813561386e6136e782614f26565b81815291506020808301908481018184028601820187101561388f57600080fd5b60005b8481101561148b57813584529282019290820190600101613892565b600082601f8301126138be578081fd5b81516138cc6136e782614f26565b8181529150602080830190848101818402860182018710156138ed57600080fd5b60005b8481101561148b578151845292820192908201906001016138f0565b80516001600160e01b0319811681146106cd57600080fd5b600082601f830112613934578081fd5b81356139426136e782614f46565b915080825283602082850101111561395957600080fd5b8060208401602084013760009082016020015292915050565b600082601f830112613982578081fd5b81516139906136e782614f46565b91508082528360208285010111156139a757600080fd5b611490816020840160208601614f6a565b8051600581106106cd57600080fd5b60006101c08083850312156139da578182fd5b6139e381614eff565b9150506139f083836136b3565b81526139ff83602084016136b3565b6020820152613a1183604084016136b3565b6040820152613a2383606084016136b3565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115613a8557600080fd5b613a9186838701613924565b83850152610160925082850135915080821115613aad57600080fd5b613ab986838701613924565b83850152610180925082850135915080821115613ad557600080fd5b613ae186838701613924565b838501526101a0925082850135915080821115613afd57600080fd5b50613b0a85828601613924565b82840152505092915050565b60006101c0808385031215613b29578182fd5b613b3281614eff565b915050613b3f83836136be565b8152613b4e83602084016136be565b6020820152613b6083604084016136be565b6040820152613b7283606084016136be565b60608201526080820151608082015260a082015160a082015260c082015160c082015260e082015160e08201526101008083015181830152506101208083015181830152506101408083015167ffffffffffffffff80821115613bd457600080fd5b613be086838701613972565b83850152610160925082850151915080821115613bfc57600080fd5b613c0886838701613972565b83850152610180925082850151915080821115613c2457600080fd5b613c3086838701613972565b838501526101a0925082850151915080821115613c4c57600080fd5b50613b0a85828601613972565b600060208284031215613c6a578081fd5b813561184a81614fa4565b60008060408385031215613c87578081fd5b8235613c9281614fa4565b9150602083013567ffffffffffffffff811115613cad578182fd5b613cb985828601613735565b9150509250929050565b60008060008060808587031215613cd8578182fd5b8435613ce381614fa4565b9350602085013567ffffffffffffffff80821115613cff578384fd5b613d0b88838901613850565b94506040870135915080821115613d20578384fd5b613d2c88838901613850565b93506060870135915080821115613d41578283fd5b50613d4e87828801613924565b91505092959194509250565b60008060408385031215613d6c578182fd5b8235613d7781614fa4565b9150602083013567ffffffffffffffff811115613d92578182fd5b613cb985828601613924565b600080600060608486031215613db2578081fd5b8335613dbd81614fa4565b9250602084013567ffffffffffffffff811115613dd8578182fd5b613de486828701613924565b925050604084013590509250925092565b60008060408385031215613e07578182fd5b8235613e1281614fa4565b946020939093013593505050565b600060208284031215613e31578081fd5b813567ffffffffffffffff811115613e47578182fd5b610934848285016136c9565b60006020808385031215613e65578182fd5b825167ffffffffffffffff811115613e7b578283fd5b80840185601f820112613e8c578384fd5b80519150613e9c6136e783614f26565b8281528381019082850185850284018601891015613eb8578687fd5b8693505b84841015613ee257613ece89826139b8565b835260019390930192918501918501613ebc565b50979650505050505050565b600080600060608486031215613f02578081fd5b833567ffffffffffffffff80821115613f19578283fd5b613f25878388016137f5565b94506020860135915080821115613f3a578283fd5b613f46878388016136c9565b93506040860135915080821115613f5b578283fd5b50613f6886828701613850565b9150509250925092565b60008060408385031215613f84578182fd5b823567ffffffffffffffff80821115613f9b578384fd5b613fa7868387016137f5565b93506020850135915080821115613fbc578283fd5b50613cb985828601613735565b60008060408385031215613fdb578182fd5b823567ffffffffffffffff80821115613ff2578384fd5b613fa786838701613850565b60006020828403121561400f578081fd5b8151801515811461184a578182fd5b60006020828403121561402f578081fd5b61184a838361390c565b6000806040838503121561404b578182fd5b614055848461390c565b9150602083015161406581614fa4565b809150509250929050565b60008060008060808587031215614085578182fd5b845161409081614fb9565b60208601519094506140a181614fa4565b60408601519093506140b281614fa4565b606086015190925067ffffffffffffffff8111156140ce578182fd5b613d4e87828801613972565b600080600080600060a086880312156140f1578283fd5b6140fb878761390c565b9450602086015161410b81614fa4565b604087015190945067ffffffffffffffff80821115614128578485fd5b61413489838a016138ae565b94506060880151915080821115614149578283fd5b61415589838a016138ae565b9350608088015191508082111561416a578283fd5b5061417788828901613972565b9150509295509295909350565b60008060008060808587031215614199578182fd5b6141a3868661390c565b935060208501516141b381614fa4565b604086015190935067ffffffffffffffff8111156141cf578283fd5b6141db87828801613972565b606096909601519497939650505050565b600080600060608486031215614200578081fd5b61420a858561390c565b9250602084015161421a81614fa4565b80925050604084015190509250925092565b600080600060608486031215614240578081fd5b835161424b81614fb9565b602085015190935067ffffffffffffffff80821115614268578283fd5b614274878388016138ae565b93506040860151915080821115614289578283fd5b50613f688682870161379a565b6000602082840312156142a7578081fd5b813567ffffffffffffffff8111156142bd578182fd5b61093484828501613924565b6000602082840312156142da578081fd5b815167ffffffffffffffff8111156142f0578182fd5b61093484828501613972565b60006020828403121561430d578081fd5b815161184a81614fa4565b600060208284031215614329578081fd5b61184a83836139b8565b60008060008060808587031215614348578182fd5b845167ffffffffffffffff8082111561435f578384fd5b61436b88838901613972565b9550602091508187015181811115614381578485fd5b80880189601f820112614392578586fd5b805191506143a26136e783614f26565b82815284810190828601885b858110156143d7576143c58e898451880101613b16565b845292870192908701906001016143ae565b505060408b01519098509450505050808211156143f2578384fd5b6143fe888389016138ae565b93506060870151915080821115614413578283fd5b50613d4e8782880161379a565b600060608284031215614431578081fd5b61443b6060614eff565b825160078110614449578283fd5b8152602083810151908201526040928301519281019290925250919050565b60008060006060848603121561447c578081fd5b833567ffffffffffffffff811115614492578182fd5b61449e868287016139c7565b93505060208401356144af81614fa4565b929592945050506040919091013590565b600080604083850312156144d2578182fd5b823567ffffffffffffffff808211156144e9578384fd5b6144f5868387016139c7565b9350602085013591508082111561450a578283fd5b50613cb985828601613924565b60008060006060848603121561452b578081fd5b833567ffffffffffffffff811115614541578182fd5b61454d868287016139c7565b93505060208401359150604084013561456581614fa4565b809150509250925092565b600080600060608486031215614584578081fd5b833567ffffffffffffffff8082111561459b578283fd5b81860160a081890312156145ad578384fd5b6145b760a0614eff565b925080358352602081013560208401526040810135604084015260608101356145df81614fa4565b60608401526080810135828111156145f5578485fd5b61460189828401613924565b60808501525091945050506020840135915061462085604086016136b3565b90509250925092565b60006020828403121561463a578081fd5b5051919050565b6001600160a01b0316815260200190565b600061465e8383614731565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b848110156146be5782840388526146a8848351614705565b6020988901989094509190910190600101614690565b50919695505050505050565b6000815180845260208401935060208301825b828110156146fb5781518652602095860195909101906001016146dd565b5093949350505050565b6000815180845261471d816020860160208601614f6a565b601f01601f19169290920160200192915050565b80516007811061473d57fe5b825260208181015190830152604090810151910152565b60006101c0614764848451614666565b60208301516147766020860182614666565b5060408301516147896040860182614666565b50606083015161479c6060860182614666565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e08501526101008084015181860152506101208084015181860152506101408084015182828701526147f583870182614705565b915050610160915081840151858203838701526148128282614705565b92505050610180808401518583038287015261482e8382614705565b9150506101a09150818401518582038387015261484b8282614705565b9695505050505050565b60008251614867818460208701614f6a565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516148c68185614bc6565b91508193508281028201838a01865b838110156148ff5786830385526148ed838351614754565b948601949250908501906001016148d5565b505086810360408801528094508851925061491a8382614bc6565b94505050818701845b8281101561494457614936858351614641565b945090830190600101614923565b50505050828103606084015261495a81856146ca565b979650505050505050565b60006001600160a01b03861682526080602083015261498760808301866146ca565b828103604084015261499981866146ca565b83810360608501526149ab8186614705565b98975050505050505050565b60006001600160a01b0385168252606060208301526149d96060830185614705565b9050826040830152949350505050565b60006001600160a01b03808716835260806020840152614a0c6080840187614754565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b81811015614a78578351614a6481614f9a565b835260209384019390920191600101614a51565b509095945050505050565b60006060820160608352808651614a9a8184614bc6565b915060209250828801845b82811015614ac657614ab8848351614652565b935090840190600101614aa5565b50505083810382850152614ada81876146ca565b84810360408601528551808252908301915082860190845b81811015614b10578251151584529284019291840191600101614af2565b509198975050505050505050565b60006020825261184a60208301846146ca565b600060408252614b4460408301856146ca565b602083820381850152818551808452828401915082838202850101838801865b83811015614b9257601f19878403018552614b80838351614705565b94860194925090850190600101614b64565b50909998505050505050505050565b600060408252614bb460408301856146ca565b828103602084015261190d81856146ca565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b0380861660208401528085166040840152506080606083015261484b6080830184614705565b60006001600160e01b0319871682526001600160a01b038616602083015260a06040830152614c7560a08301866146ca565b8281036060840152614c8781866146ca565b8381036080850152614c998186614705565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614cd86080830185614705565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614d3760608301856146ca565b828103604084015261484b8185614673565b60006020825261184a6020830184614705565b600060808252614d6f6080830187614705565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614da857fe5b938152602081019290925260409091015290565b6060810160088510614da857fe5b60208101614dd783614f9a565b91905290565b600060808252614df06080830187614705565b602083820381850152818751808452828401915082838202850101838a01865b83811015614e3e57601f19878403018552614e2c838351614754565b94860194925090850190600101614e10565b50508681036040880152614e52818a6146ca565b945050505050828103606084015261495a8185614673565b60a08101614e788286614731565b8360608301528215156080830152949350505050565b60006020825261184a6020830184614754565b600060408252614eb46040830185614754565b90506001600160a01b03831660208301529392505050565b600060408252614edf6040830185614754565b828103602084015261190d8185614705565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614f1e57600080fd5b604052919050565b600067ffffffffffffffff821115614f3c578081fd5b5060209081020190565b600067ffffffffffffffff821115614f5c578081fd5b50601f01601f191660200190565b60005b83811015614f85578181015183820152602001614f6d565b83811115614f94576000848401525b50505050565b6005811061075457fe5b6001600160a01b038116811461075457600080fd5b6001600160e01b03198116811461075457600080fdfea365627a7a72315820f05bfff91cf6f387ce89e4ca5ccc415a4c291e39b349e6d1c630a5574b2fee306c6578706572696d656e74616cf564736f6c63430005100040" }, "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b506004361061025c5760003560e01c8063a5cd62ba11610145578063d3637905116100bd578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105b5578063ef3bb097146105bd578063ff84e7cc146105c55761025c565b8063e4e6e7da14610572578063e77286eb146105935761025c565b8063d36379051461050a578063d3d862d11461051d578063d469502814610530578063e25cabf7146105505761025c565b8063c26cfecd11610114578063ca49f47c116100f9578063ca49f47c146104c1578063d001c5dc146104e4578063d186037f146104f75761025c565b8063c26cfecd146104b1578063c82037ef146104b95761025c565b8063a5cd62ba14610449578063a6627e9f14610469578063b43cffe11461047c578063bbb2dcf61461048f5761025c565b80637982653e116101d85780639baf2705116101a75780639eadc8351161018c5780639eadc835146103ff578063a070cac814610423578063a0901e51146104365761025c565b80639baf2705146103e25780639cd01605146103f75761025c565b80637982653e1461037b57806379c9c4261461039b5780637d727512146103ae5780638f4ce479146103c15761025c565b80634dfdac201161022f57806363eb39921161021457806363eb3992146103225780636f83188e14610335578063750bdb30146103585761025c565b80634dfdac20146102e2578063590aa875146103025761025c565b806304a5618a146102615780630d7b7d761461028c5780632322cf76146102ad57806346eb65cb146102cd575b600080fd5b61027461026f366004613e8f565b6105cd565b60405161028393929190614a5a565b60405180910390f35b61029f61029a366004613953565b610666565b604051610283929190614c37565b6102c06102bb366004613953565b610688565b6040516102839190614937565b6102e06102db366004613e8f565b6106b0565b005b6102f56102f036600461386e565b610734565b604051610283919061488f565b610315610310366004613852565b6107b7565b6040516102839190614aba565b610315610330366004613997565b610866565b610348610343366004613e8f565b610919565b6040516102839493929190614b4e565b61036b610366366004613e8f565b6109b9565b6040516102839493929190614978565b61038e6103893660046141da565b610a4b565b6040516102839190614b3b565b6102c06103a93660046142e2565b610afd565b6102c06103bc366004613953565b610b7e565b6103d46103cf366004613e8f565b611474565b604051610283929190614955565b6103ea611507565b60405161028391906145e2565b6103ea611516565b61041261040d366004613e8f565b611525565b6040516102839594939291906149b4565b6102c0610431366004614289565b6115ca565b6102f5610444366004613a19565b61164b565b61045c610457366004613ae7565b6116c4565b60405161028391906147a8565b6103156104773660046139ee565b61177e565b61031561048a3660046138bc565b61182e565b6104a261049d366004613e8f565b6118e4565b60405161028393929190614a85565b6102c0611974565b6103ea61197a565b6104d46104cf366004613e8f565b611989565b6040516102839493929190614a17565b6102f56104f236600461386e565b611a1b565b6102c0610505366004613953565b611a89565b61038e6105183660046141da565b612365565b61031561052b366004613bc2565b6123c7565b61054361053e366004613e8f565b61241c565b6040516102839190614940565b61056361055e366004613b6b565b6124bf565b604051610283939291906147f4565b61058561058036600461386e565b6125f7565b604051610283929190614912565b6105a66105a1366004614232565b612610565b60405161028393929190614bdb565b6103ea61287f565b6103ea61288e565b6103ea61289d565b600080600073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a856040518263ffffffff1660e01b81526004016106099190614aba565b60606040518083038186803b15801561062157600080fd5b505af4158015610635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106599190810190613de5565b9250925092509193909250565b6000806106738484610b7e565b915061067f8484611a89565b90509250929050565b60008060006106978585610666565b915091506106a582826128ac565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$dca6deb482199a44a74cce8be70bfdd753$__906346eb65cb90610700908490600401614aba565b60006040518083038186803b15801561071857600080fd5b505af415801561072c573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610766578160200160208202803883390190505b50915060005b8181146107af576107908585838151811061078357fe5b6020026020010151611a89565b83828151811061079c57fe5b602090810291909101015260010161076c565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063590aa8759061080a9085906004016145e2565b60006040518083038186803b15801561082257600080fd5b505af4158015610836573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261085e9190810190613ec2565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__906363eb3992906108bd90879087908790600401614728565b60006040518083038186803b1580156108d557600080fd5b505af41580156108e9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613ec2565b949350505050565b60608060608073__$d88c3928727cb33b5732520dfb08856c25$__636f83188e866040518263ffffffff1660e01b81526004016109569190614aba565b60006040518083038186803b15801561096e57600080fd5b505af4158015610982573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613f30565b93509350935093509193509193565b6000806000606073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016109f79190614aba565b60006040518083038186803b158015610a0f57600080fd5b505af4158015610a23573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613c69565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91639caa023b91610aad916001600160a01b03169088908890889060040161475a565b60206040518083038186803b158015610ac557600080fd5b505af4158015610ad9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109119190810190613f11565b600061091184610b796040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612919565b600080610b91838263ffffffff61292d16565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610bec576000610bd884601063ffffffff61296616565b9050610be48186612999565b92505061146d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e055760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401610c589190614aba565b60606040518083038186803b158015610c7057600080fd5b505af4158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ca89190810190613de5565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610ce4908490602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d5f91906145c6565b600060405180830381855afa9150503d8060008114610d9a576040519150601f19603f3d011682016040523d82523d6000602084013e610d9f565b606091505b50915091506000828015610db4575081516020145b610dbf576000610dd0565b610dd082600c63ffffffff61296616565b9050896001600160a01b0316816001600160a01b031614610df2576000610df5565b60015b60ff16975050505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110a157600060608073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b8152600401610e739190614aba565b60006040518083038186803b158015610e8b57600080fd5b505af4158015610e9f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ec79190810190613cd3565b5081519296509094509250905060005b81811461109757828181518110610eea57fe5b602002602001015160001415610eff5761108f565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f3357fe5b6020026020010151604051602401610f4c92919061478f565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fc791906145c6565b600060405180830381855afa9150503d8060008114611002576040519150601f19603f3d011682016040523d82523d6000602084013e611007565b606091505b5091509150600082801561101c575081516020145b611027576000611038565b61103882600063ffffffff612a9116565b9050600087868151811061104857fe5b6020026020010151828161105857fe5b049050806110745760009b5050505050505050505050506106aa565b8b81108061108057508b155b1561108957809b505b50505050505b600101610ed7565b505050505061146d565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111d4576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061111090869060009081908190602401614acd565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b0316906111779084906145c6565b600060405180830381855afa9150503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b50509050806111c75760006111cb565b6000195b9350505061146d565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156112fd5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016112409190614aba565b60006040518083038186803b15801561125857600080fd5b505af415801561126c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112949190810190613c69565b5092509250506112a2612a9d565b6001600160a01b0316826001600160a01b03161480156112cf57506005546001600160a01b038281169116145b156112f65760006112e76112e1612ab5565b88612999565b90506112f281612acd565b9450505b505061146d565b6001600160e01b031981167f94cfcdd700000000000000000000000000000000000000000000000000000000141561146d5760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b81526004016113699190614aba565b60006040518083038186803b15801561138157600080fd5b505af4158015611395573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113bd9190810190613e25565b80519194509250905060005b818114611468578381815181106113dc57fe5b6020026020010151600014156113f157611460565b60006114108985848151811061140357fe5b6020026020010151610b7e565b9050600085838151811061142057fe5b6020026020010151828161143057fe5b049050806114485760009750505050505050506106aa565b87811080611454575087155b1561145d578097505b50505b6001016113c9565b505050505b5092915050565b60008073__$dca6deb482199a44a74cce8be70bfdd753$__638f4ce479846040518263ffffffff1660e01b81526004016114ae9190614aba565b604080518083038186803b1580156114c557600080fd5b505af41580156114d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114fd9190810190613c32565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b81526004016115649190614aba565b60006040518083038186803b15801561157c57600080fd5b505af4158015611590573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b89190810190613cd3565b939a9299509097509550909350915050565b6000610911846116466040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612ccb565b6060808251604051908082528060200260200182016040528015611679578160200160208202803883390190505b50905060005b8351811461146d5783818151811061169357fe5b60200260200101516001600160a01b0316318282815181106116b157fe5b602090810291909101015260010161167f565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$0c5fffa094d709a948ceda4c4b1013b697$__916302cffc459161172a916001600160a01b0390911690889088908890600401614610565b60006040518083038186803b15801561174257600080fd5b505af4158015611756573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613a4c565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063a6627e9f906117d3908690869060040161478f565b60006040518083038186803b1580156117eb57600080fd5b505af41580156117ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118279190810190613ec2565b9392505050565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063b43cffe1906118879088908890889088906004016146d6565b60006040518083038186803b15801561189f57600080fd5b505af41580156118b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118db9190810190613ec2565b95945050505050565b600060608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119209190614aba565b60006040518083038186803b15801561193857600080fd5b505af415801561194c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106599190810190613e25565b60065481565b6005546001600160a01b031681565b6000806060600073__$dca6deb482199a44a74cce8be70bfdd753$__63ca49f47c866040518263ffffffff1660e01b81526004016119c79190614aba565b60006040518083038186803b1580156119df57600080fd5b505af41580156119f3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613d7d565b606060008251905080604051908082528060200260200182016040528015611a4d578160200160208202803883390190505b50915060005b8181146107af57611a6a8585838151811061140357fe5b838281518110611a7657fe5b6020908102919091010152600101611a53565b600080611a9c838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c075760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b0a9190614aba565b60006040518083038186803b158015611b2257600080fd5b505af4158015611b36573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b5e9190810190613e25565b80519194509250905060005b818114611bfc57838181518110611b7d57fe5b602002602001015160001415611b9257611bf4565b6000611ba48985848151811061078357fe5b90506000858381518110611bb457fe5b60200260200101518281611bc457fe5b04905080611bdc5760009750505050505050506106aa565b87811080611be8575087155b15611bf1578097505b50505b600101611b6a565b506106aa9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c68576000611c4c84601063ffffffff61296616565b600154909150610be490829087906001600160a01b0316612cda565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fb85760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401611cd49190614aba565b60606040518083038186803b158015611cec57600080fd5b505af4158015611d00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d249190810190613de5565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611d70918a916001600160a01b03909116906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611deb91906145c6565b600060405180830381855afa9150503d8060008114611e26576040519150601f19603f3d011682016040523d82523d6000602084013e611e2b565b606091505b5091509150811580611e3f57508051602014155b80611e5b5750611e5681600063ffffffff612a9116565b600114155b15611fa9576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611e96908790602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f0d91906145c6565b600060405180830381855afa9150503d8060008114611f48576040519150601f19603f3d011682016040523d82523d6000602084013e611f4d565b606091505b509093509150828015611f61575081516020145b8015611f9057506002546001600160a01b0316611f8583600c63ffffffff61296616565b6001600160a01b0316145b611f9b576000611f9e565b60015b60ff16975050611097565b6000199650505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156121df576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__90639eadc8359061203d908790600401614aba565b60006040518083038186803b15801561205557600080fd5b505af4158015612069573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120919190810190613cd3565b5050600354604051929450606093507fe985e9c500000000000000000000000000000000000000000000000000000000926120db925089916001600160a01b0316906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b03168360405161215691906145c6565b600060405180830381855afa9150503d8060008114612191576040519150601f19603f3d011682016040523d82523d6000602084013e612196565b606091505b50915091508180156121a9575080516020145b80156121c557506121c181600063ffffffff612a9116565b6001145b6121d05760006121d4565b6000195b95505050505061146d565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561221b57600019915061146d565b6001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561146d5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016122879190614aba565b60006040518083038186803b15801561229f57600080fd5b505af41580156122b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122db9190810190613c69565b5092509250506122e9612a9d565b6001600160a01b0316826001600160a01b031614801561231657506005546001600160a01b038281169116145b1561235c57600061233b612328612ab5565b60055489906001600160a01b0316612cda565b905060001981146123545761234f81612acd565b612358565b6000195b9450505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91638dccde0491610aad916001600160a01b03169088908890889060040161475a565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063d3d862d1906117d390869086906004016148a2565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__9063d46950289061246f908590600401614aba565b60206040518083038186803b15801561248757600080fd5b505af415801561249b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061085e9190810190613c17565b60608060606000855190508060405190808252806020026020018201604052801561250457816020015b6124f16133cd565b8152602001906001900390816124e95790505b50935080604051908082528060200260200182016040528015612531578160200160208202803883390190505b5092508060405190808252806020026020018201604052801561255e578160200160208202803883390190505b50915060005b8181146125ee5761259b87828151811061257a57fe5b602002602001015187838151811061258e57fe5b6020026020010151612610565b87518890859081106125a957fe5b602002602001018785815181106125bc57fe5b602002602001018786815181106125cf57fe5b9315156020948502919091019093019290925291905252600101612564565b50509250925092565b6060806126048484611a1b565b915061067f8484610734565b6126186133cd565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b990612662908890600401614bff565b60606040518083038186803b15801561267a57600080fd5b505afa15801561268e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126b29190810190614192565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127049089908990600401614c12565b60206040518083038186803b15801561271c57600080fd5b505afa158015612730573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127549190810190613bf7565b9150600061276187612de4565b90506000612782886101800151896101400151612e0090919063ffffffff16565b156127b7576127b0826127a68a60c001518b60800151612e2590919063ffffffff16565b8a60a00151612e41565b9050612824565b60c08801516127d3576127b08289608001518a60a00151612e41565b60006127e4848a6101800151610688565b905060006127fb848b608001518c60a00151612e41565b90506000612812838c60c001518d60a00151612e41565b905061281e82826128ac565b93505050505b61284861284287604001518a60a00151612e6390919063ffffffff16565b826128ac565b945061285388612e82565b61285c57600094505b60038651600681111561286b57fe5b1461287557600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b60008183106128bb5781611827565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b60006118278261292885612ee9565b612f64565b600081600401835110156129535761295361294e6003855185600401612f9e565b61300d565b5001602001516001600160e01b03191690565b600081601401835110156129875761298761294e6004855185601401612f9e565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a0823100000000000000000000000000000000000000000000000000000000906129df9087906024016145e2565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612a3291906145c6565b600060405180830381855afa9150503d8060008114612a6d576040519150601f19603f3d011682016040523d82523d6000602084013e612a72565b606091505b5091509150818015612a85575080516020145b156107af576106a58160005b60006118278383613015565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612ad8612ab5565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b1257600080fd5b505af1158015612b26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b4a9190810190613ef5565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b8957600080fd5b505af1158015612b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bc1919081019061439a565b4211612c3f57816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c3a919081019061439a565b612cb2565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c7a57600080fd5b505af1158015612c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cb2919081019061439a565b9050610911816b033b2e3c9fd0803ce800000086612e41565b6000611827826129288561303f565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612d2290889088906024016145f6565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612d7591906145c6565b600060405180830381855afa9150503d8060008114612db0576040519150601f19603f3d011682016040523d82523d6000602084013e612db5565b606091505b5091509150818015612dc8575080516020145b15612ddb57612dd8816000612a91565b92505b50509392505050565b6000806000612df2846130f8565b9150915061091182826128ac565b6000815183511480156118275750508051602091820120825192909101919091201490565b6000828201838110156118275761182761294e6000868661313b565b600061091183612e57868563ffffffff61315a16565b9063ffffffff61318b16565b600082821115612e7c57612e7c61294e6002858561313b565b50900390565b6000612e928261014001516131b5565b8015612eb3575060c08201511580612eb35750612eb38261018001516131b5565b8015612ec85750612ec88261016001516131b5565b801561085e575060e0820151158061085e575061085e826101a001516131b5565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b848484604051602401612fbd93929190614b2d565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156130365761303661294e6005855185602001612f9e565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561308b57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b600080600483610140015151101561311557506000905080611502565b6131288360000151846101400151610b7e565b6114fd8460000151856101400151611a89565b606063e946c1bb60e01b848484604051602401612fbd93929190614b0b565b600082613169575060006106aa565b8282028284828161317657fe5b04146118275761182761294e6001868661313b565b6000816131a1576131a161294e6003858561313b565b60008284816131ac57fe5b04949350505050565b600060208251816131c257fe5b066004146131d257506000610861565b60006131e4838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613221576001915050610861565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063bbb2dcf690613274908790600401614aba565b60006040518083038186803b15801561328c57600080fd5b505af41580156132a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132c89190810190613e25565b80519093509150600090505b81811461335e57600061330460008584815181106132ee57fe5b602002602001015161292d90919063ffffffff16565b90506001600160e01b031981167f0257179200000000000000000000000000000000000000000000000000000000141561335557613342848361336a565b1561335557600095505050505050610861565b506001016132d4565b50600195945050505050565b8151600090600183015b818110156107af576133b585828151811061338b57fe5b602002602001015186868151811061339f57fe5b6020026020010151612e0090919063ffffffff16565b156133c5576001925050506106aa565b600101613374565b6040805160608101909152806000815260006020820181905260409091015290565b80356106aa81614cea565b80516106aa81614cea565b600082601f830112613415578081fd5b813561342861342382614c6c565b614c45565b81815291506020808301908481018184028601820187101561344957600080fd5b60005b8481101561146857813561345f81614cea565b8452928201929082019060010161344c565b600082601f830112613481578081fd5b813561348f61342382614c6c565b8181529150602080830190840160005b838110156134cc576134b78760208435890101613660565b8352602092830192919091019060010161349f565b5050505092915050565b600082601f8301126134e6578081fd5b81516134f461342382614c6c565b8181529150602080830190840160005b838110156134cc5761351c87602084518901016136ae565b83526020928301929190910190600101613504565b600082601f830112613541578081fd5b813561354f61342382614c6c565b8181529150602080830190840160005b838110156134cc576135778760208435890101613703565b8352602092830192919091019060010161355f565b600082601f83011261359c578081fd5b81356135aa61342382614c6c565b8181529150602080830190848101818402860182018710156135cb57600080fd5b60005b84811015611468578135845292820192908201906001016135ce565b600082601f8301126135fa578081fd5b815161360861342382614c6c565b81815291506020808301908481018184028601820187101561362957600080fd5b60005b848110156114685781518452928201929082019060010161362c565b80516001600160e01b0319811681146106aa57600080fd5b600082601f830112613670578081fd5b813561367e61342382614c8c565b915080825283602082850101111561369557600080fd5b8060208401602084013760009082016020015292915050565b600082601f8301126136be578081fd5b81516136cc61342382614c8c565b91508082528360208285010111156136e357600080fd5b61146d816020840160208601614cb0565b8051600581106106aa57600080fd5b60006101c0808385031215613716578182fd5b61371f81614c45565b91505061372c83836133ef565b815261373b83602084016133ef565b602082015261374d83604084016133ef565b604082015261375f83606084016133ef565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156137c157600080fd5b6137cd86838701613660565b838501526101609250828501359150808211156137e957600080fd5b6137f586838701613660565b8385015261018092508285013591508082111561381157600080fd5b61381d86838701613660565b838501526101a092508285013591508082111561383957600080fd5b5061384685828601613660565b82840152505092915050565b600060208284031215613863578081fd5b813561182781614cea565b60008060408385031215613880578081fd5b823561388b81614cea565b9150602083013567ffffffffffffffff8111156138a6578182fd5b6138b285828601613471565b9150509250929050565b600080600080608085870312156138d1578182fd5b84356138dc81614cea565b9350602085013567ffffffffffffffff808211156138f8578384fd5b6139048883890161358c565b94506040870135915080821115613919578384fd5b6139258883890161358c565b9350606087013591508082111561393a578283fd5b5061394787828801613660565b91505092959194509250565b60008060408385031215613965578182fd5b823561397081614cea565b9150602083013567ffffffffffffffff81111561398b578182fd5b6138b285828601613660565b6000806000606084860312156139ab578081fd5b83356139b681614cea565b9250602084013567ffffffffffffffff8111156139d1578182fd5b6139dd86828701613660565b925050604084013590509250925092565b60008060408385031215613a00578182fd5b8235613a0b81614cea565b946020939093013593505050565b600060208284031215613a2a578081fd5b813567ffffffffffffffff811115613a40578182fd5b61091184828501613405565b60006020808385031215613a5e578182fd5b825167ffffffffffffffff811115613a74578283fd5b80840185601f820112613a85578384fd5b80519150613a9561342383614c6c565b8281528381019082850185850284018601891015613ab1578687fd5b8693505b84841015613adb57613ac789826136f4565b835260019390930192918501918501613ab5565b50979650505050505050565b600080600060608486031215613afb578081fd5b833567ffffffffffffffff80821115613b12578283fd5b613b1e87838801613531565b94506020860135915080821115613b33578283fd5b613b3f87838801613405565b93506040860135915080821115613b54578283fd5b50613b618682870161358c565b9150509250925092565b60008060408385031215613b7d578182fd5b823567ffffffffffffffff80821115613b94578384fd5b613ba086838701613531565b93506020850135915080821115613bb5578283fd5b506138b285828601613471565b60008060408385031215613bd4578182fd5b823567ffffffffffffffff80821115613beb578384fd5b613ba08683870161358c565b600060208284031215613c08578081fd5b81518015158114611827578182fd5b600060208284031215613c28578081fd5b6118278383613648565b60008060408385031215613c44578182fd5b613c4e8484613648565b91506020830151613c5e81614cea565b809150509250929050565b60008060008060808587031215613c7e578182fd5b8451613c8981614cff565b6020860151909450613c9a81614cea565b6040860151909350613cab81614cea565b606086015190925067ffffffffffffffff811115613cc7578182fd5b613947878288016136ae565b600080600080600060a08688031215613cea578283fd5b613cf48787613648565b94506020860151613d0481614cea565b604087015190945067ffffffffffffffff80821115613d21578485fd5b613d2d89838a016135ea565b94506060880151915080821115613d42578283fd5b613d4e89838a016135ea565b93506080880151915080821115613d63578283fd5b50613d70888289016136ae565b9150509295509295909350565b60008060008060808587031215613d92578182fd5b613d9c8686613648565b93506020850151613dac81614cea565b604086015190935067ffffffffffffffff811115613dc8578283fd5b613dd4878288016136ae565b606096909601519497939650505050565b600080600060608486031215613df9578081fd5b613e038585613648565b92506020840151613e1381614cea565b80925050604084015190509250925092565b600080600060608486031215613e39578081fd5b8351613e4481614cff565b602085015190935067ffffffffffffffff80821115613e61578283fd5b613e6d878388016135ea565b93506040860151915080821115613e82578283fd5b50613b61868287016134d6565b600060208284031215613ea0578081fd5b813567ffffffffffffffff811115613eb6578182fd5b61091184828501613660565b600060208284031215613ed3578081fd5b815167ffffffffffffffff811115613ee9578182fd5b610911848285016136ae565b600060208284031215613f06578081fd5b815161182781614cea565b600060208284031215613f22578081fd5b815160058110611827578182fd5b60008060008060808587031215613f45578182fd5b845167ffffffffffffffff811115613f5b578283fd5b613f67878288016136ae565b945050602085015167ffffffffffffffff811115613f83578283fd5b86601f8288010112613f93578283fd5b80860151613fa361342382614c6c565b8181526020808201919089850101865b8481101561413c578151868c01016101c0601f19828f03011215613fd5578889fd5b613fe06101c0614c45565b613fed8e602084016133fa565b8152613ffc8e604084016133fa565b602082015261400e8e606084016133fa565b60408201526140208e608084016133fa565b606082015260a0820151608082015260c082015160a082015260e082015160c082015261010082015160e082015261012082015161010082015261014082015161012082015261016082015167ffffffffffffffff811115614080578a8bfd5b61408f8f6020838601016136ae565b6101408301525061018082015167ffffffffffffffff8111156140b0578a8bfd5b6140bf8f6020838601016136ae565b610160830152506101a082015167ffffffffffffffff8111156140e0578a8bfd5b6140ef8f6020838601016136ae565b610180830152506101c082015167ffffffffffffffff811115614110578a8bfd5b61411f8f6020838601016136ae565b6101a0830152508552506020938401939190910190600101613fb3565b505080965050505050604085015167ffffffffffffffff81111561415e578283fd5b61416a878288016135ea565b925050606085015167ffffffffffffffff811115614186578182fd5b613947878288016134d6565b6000606082840312156141a3578081fd5b6141ad6060614c45565b8251600781106141bb578283fd5b8152602083810151908201526040928301519281019290925250919050565b6000806000606084860312156141ee578081fd5b833567ffffffffffffffff811115614204578182fd5b61421086828701613703565b935050602084013561422181614cea565b929592945050506040919091013590565b60008060408385031215614244578182fd5b823567ffffffffffffffff8082111561425b578384fd5b61426786838701613703565b9350602085013591508082111561427c578283fd5b506138b285828601613660565b60008060006060848603121561429d578081fd5b833567ffffffffffffffff8111156142b3578182fd5b6142bf86828701613703565b9350506020840135915060408401356142d781614cea565b809150509250925092565b6000806000606084860312156142f6578081fd5b833567ffffffffffffffff8082111561430d578283fd5b81860160a0818903121561431f578384fd5b61432960a0614c45565b925080358352602081013560208401526040810135604084015261435088606083016133ef565b6060840152608081013582811115614366578485fd5b61437289828401613660565b60808501525091945050506020840135915061439185604086016133ef565b90509250925092565b6000602082840312156143ab578081fd5b5051919050565b6001600160a01b0316815260200190565b60006143cf83836144a2565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b8481101561442f578284038852614419848351614476565b6020988901989094509190910190600101614401565b50919695505050505050565b6000815180845260208401935060208301825b8281101561446c57815186526020958601959091019060010161444e565b5093949350505050565b6000815180845261448e816020860160208601614cb0565b601f01601f19169290920160200192915050565b8051600781106144ae57fe5b825260208181015190830152604090810151910152565b60006101c06144d58484516143d7565b60208301516144e760208601826143d7565b5060408301516144fa60408601826143d7565b50606083015161450d60608601826143d7565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010080840151818601525061012080840151818601525061014080840151828287015261456683870182614476565b915050610160915081840151858203838701526145838282614476565b92505050610180808401518583038287015261459f8382614476565b9150506101a0915081840151858203838701526145bc8282614476565b9695505050505050565b600082516145d8818460208701614cb0565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516146378185614937565b91508193508281028201838a01865b8381101561467057868303855261465e8383516144c5565b94860194925090850190600101614646565b505086810360408801528094508851925061468b8382614937565b94505050818701845b828110156146b5576146a78583516143b2565b945090830190600101614694565b5050505082810360608401526146cb818561443b565b979650505050505050565b60006001600160a01b0386168252608060208301526146f8608083018661443b565b828103604084015261470a818661443b565b838103606085015261471c8186614476565b98975050505050505050565b60006001600160a01b03851682526060602083015261474a6060830185614476565b9050826040830152949350505050565b60006001600160a01b0380871683526080602084015261477d60808401876144c5565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b818110156147e95783516147d581614ce0565b8352602093840193909201916001016147c2565b509095945050505050565b6000606082016060835280865161480b8184614937565b915060209250828801845b82811015614837576148298483516143c3565b935090840190600101614816565b5050508381038285015261484b818761443b565b84810360408601528551808252908301915082860190845b81811015614881578251151584529284019291840191600101614863565b509198975050505050505050565b600060208252611827602083018461443b565b6000604082526148b5604083018561443b565b602083820381850152818551808452828401915082838202850101838801865b8381101561490357601f198784030185526148f1838351614476565b948601949250908501906001016148d5565b50909998505050505050505050565b600060408252614925604083018561443b565b82810360208401526118db818561443b565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b038086166020840152808516604084015250608060608301526145bc6080830184614476565b60006001600160e01b0319871682526001600160a01b038616602083015260a060408301526149e660a083018661443b565b82810360608401526149f8818661443b565b8381036080850152614a0a8186614476565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614a496080830185614476565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614aa8606083018561443b565b82810360408401526145bc81856143e4565b6000602082526118276020830184614476565b600060808252614ae06080830187614476565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614b1957fe5b938152602081019290925260409091015290565b6060810160088510614b1957fe5b60208101614b4883614ce0565b91905290565b600060808252614b616080830187614476565b602083820381850152818751808452828401915082838202850101838a01865b83811015614baf57601f19878403018552614b9d8383516144c5565b94860194925090850190600101614b81565b50508681036040880152614bc3818a61443b565b94505050505082810360608401526146cb81856143e4565b60a08101614be982866144a2565b8360608301528215156080830152949350505050565b60006020825261182760208301846144c5565b600060408252614c2560408301856144c5565b82810360208401526118db8185614476565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614c6457600080fd5b604052919050565b600067ffffffffffffffff821115614c82578081fd5b5060209081020190565b600067ffffffffffffffff821115614ca2578081fd5b50601f01601f191660200190565b60005b83811015614ccb578181015183820152602001614cb3565b83811115614cda576000848401525b50505050565b6005811061073157fe5b6001600160a01b038116811461073157600080fd5b6001600160e01b03198116811461073157600080fdfea365627a7a72315820dc2fa6b39099cbef3d852e9f64db6bbaec7f3795508fc6ad2c43d1a719227f996c6578706572696d656e74616cf564736f6c63430005100040" + "object": "0x608060405234801561001057600080fd5b50600436106102775760003560e01c8063a5cd62ba11610160578063d186037f116100d8578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105d8578063ef3bb097146105e0578063ff84e7cc146105e857610277565b8063e4e6e7da14610595578063e77286eb146105b657610277565b8063d3d862d1116100bd578063d3d862d114610540578063d469502814610553578063e25cabf71461057357610277565b8063d186037f1461051a578063d36379051461052d57610277565b8063bbb2dcf61161012f578063c82037ef11610114578063c82037ef146104dc578063ca49f47c146104e4578063d001c5dc1461050757610277565b8063bbb2dcf6146104b2578063c26cfecd146104d457610277565b8063a5cd62ba14610464578063a6627e9f14610484578063a7530f1214610497578063b43cffe11461049f57610277565b80637982653e116101f35780639baf2705116101c25780639eadc835116101a75780639eadc8351461041a578063a070cac81461043e578063a0901e511461045157610277565b80639baf2705146103fd5780639cd016051461041257610277565b80637982653e1461039657806379c9c426146103b65780637d727512146103c95780638f4ce479146103dc57610277565b80634dfdac201161024a57806363eb39921161022f57806363eb39921461033d5780636f83188e14610350578063750bdb301461037357610277565b80634dfdac20146102fd578063590aa8751461031d57610277565b806304a5618a1461027c5780630d7b7d76146102a75780632322cf76146102c857806346eb65cb146102e8575b600080fd5b61028f61028a366004614296565b6105f0565b60405161029e93929190614ce9565b60405180910390f35b6102ba6102b5366004613d5a565b610689565b60405161029e929190614ef1565b6102db6102d6366004613d5a565b6106ab565b60405161029e9190614bc6565b6102fb6102f6366004614296565b6106d3565b005b61031061030b366004613c75565b610757565b60405161029e9190614b1e565b61033061032b366004613c59565b6107da565b60405161029e9190614d49565b61033061034b366004613d9e565b610889565b61036361035e366004614296565b61093c565b60405161029e9493929190614ddd565b610386610381366004614296565b6109dc565b60405161029e9493929190614c07565b6103a96103a4366004614468565b610a6e565b60405161029e9190614dca565b6102db6103c4366004614570565b610b20565b6102db6103d7366004613d5a565b610ba1565b6103ef6103ea366004614296565b611497565b60405161029e929190614be4565b61040561152a565b60405161029e9190614871565b610405611539565b61042d610428366004614296565b611548565b60405161029e959493929190614c43565b6102db61044c366004614517565b6115ed565b61031061045f366004613e20565b61166e565b610477610472366004613eee565b6116e7565b60405161029e9190614a37565b610330610492366004613df5565b6117a1565b610405611851565b6103306104ad366004613cc3565b611860565b6104c56104c0366004614296565b611916565b60405161029e93929190614d14565b6102db6119a6565b6104056119ac565b6104f76104f2366004614296565b6119bb565b60405161029e9493929190614ca6565b610310610515366004613c75565b611a4d565b6102db610528366004613d5a565b611abb565b6103a961053b366004614468565b6123b7565b61033061054e366004613fc9565b612419565b610566610561366004614296565b61246e565b60405161029e9190614bcf565b610586610581366004613f72565b612511565b60405161029e93929190614a83565b6105a86105a3366004613c75565b612649565b60405161029e929190614ba1565b6105c96105c43660046144c0565b612662565b60405161029e93929190614e6a565b6104056129d2565b6104056129e1565b6104056129f0565b600080600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a856040518263ffffffff1660e01b815260040161062c9190614d49565b60606040518083038186803b15801561064457600080fd5b505af4158015610658573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061067c91908101906141ec565b9250925092509193909250565b6000806106968484610ba1565b91506106a28484611abb565b90509250929050565b60008060006106ba8585610689565b915091506106c882826129ff565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$d8b635de2bf4a097b4e18b67d0fb68e779$__906346eb65cb90610723908490600401614d49565b60006040518083038186803b15801561073b57600080fd5b505af415801561074f573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610789578160200160208202803883390190505b50915060005b8181146107d2576107b3858583815181106107a657fe5b6020026020010151611abb565b8382815181106107bf57fe5b602090810291909101015260010161078f565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063590aa8759061082d908590600401614871565b60006040518083038186803b15801561084557600080fd5b505af4158015610859573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261088191908101906142c9565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__906363eb3992906108e0908790879087906004016149b7565b60006040518083038186803b1580156108f857600080fd5b505af415801561090c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261093491908101906142c9565b949350505050565b60608060608073__$fddee3b26a806429350031cf3481976d2e$__636f83188e866040518263ffffffff1660e01b81526004016109799190614d49565b60006040518083038186803b15801561099157600080fd5b505af41580156109a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614333565b93509350935093509193509193565b6000806000606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b8152600401610a1a9190614d49565b60006040518083038186803b158015610a3257600080fd5b505af4158015610a46573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614070565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91639caa023b91610ad0916001600160a01b0316908890889088906004016149e9565b60206040518083038186803b158015610ae857600080fd5b505af4158015610afc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109349190810190614318565b600061093484610b9c6040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612a6c565b600080610bb4838263ffffffff612a8016565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610c0f576000610bfb84601063ffffffff612ab916565b9050610c078186612aec565b925050611490565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e285760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401610c7b9190614d49565b60606040518083038186803b158015610c9357600080fd5b505af4158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccb91908101906141ec565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610d07908490602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d829190614855565b600060405180830381855afa9150503d8060008114610dbd576040519150601f19603f3d011682016040523d82523d6000602084013e610dc2565b606091505b50915091506000828015610dd7575081516020145b610de2576000610df3565b610df382600c63ffffffff612ab916565b9050896001600160a01b0316816001600160a01b031614610e15576000610e18565b60015b60ff169750505050505050611490565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110c457600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b8152600401610e969190614d49565b60006040518083038186803b158015610eae57600080fd5b505af4158015610ec2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610eea91908101906140da565b5081519296509094509250905060005b8181146110ba57828181518110610f0d57fe5b602002602001015160001415610f22576110b2565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f5657fe5b6020026020010151604051602401610f6f929190614a1e565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fea9190614855565b600060405180830381855afa9150503d8060008114611025576040519150601f19603f3d011682016040523d82523d6000602084013e61102a565b606091505b5091509150600082801561103f575081516020145b61104a57600061105b565b61105b82600063ffffffff612be416565b9050600087868151811061106b57fe5b6020026020010151828161107b57fe5b049050806110975760009b5050505050505050505050506106cd565b8b8110806110a357508b155b156110ac57809b505b50505050505b600101610efa565b5050505050611490565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111f7576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061113390869060009081908190602401614d5c565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b03169061119a908490614855565b600060405180830381855afa9150503d80600081146111d5576040519150601f19603f3d011682016040523d82523d6000602084013e6111da565b606091505b50509050806111ea5760006111ee565b6000195b93505050611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156113205760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016112639190614d49565b60006040518083038186803b15801561127b57600080fd5b505af415801561128f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112b79190810190614070565b5092509250506112c5612bf0565b6001600160a01b0316826001600160a01b03161480156112f257506005546001600160a01b038281169116145b1561131957600061130a611304612c08565b88612aec565b905061131581612c20565b9450505b5050611490565b6001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014156114905760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b815260040161138c9190614d49565b60006040518083038186803b1580156113a457600080fd5b505af41580156113b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113e0919081019061422c565b80519194509250905060005b81811461148b578381815181106113ff57fe5b60200260200101516000141561141457611483565b60006114338985848151811061142657fe5b6020026020010151610ba1565b9050600085838151811061144357fe5b6020026020010151828161145357fe5b0490508061146b5760009750505050505050506106cd565b87811080611477575087155b15611480578097505b50505b6001016113ec565b505050505b5092915050565b60008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__638f4ce479846040518263ffffffff1660e01b81526004016114d19190614d49565b604080518083038186803b1580156114e857600080fd5b505af41580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115209190810190614039565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b81526004016115879190614d49565b60006040518083038186803b15801561159f57600080fd5b505af41580156115b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115db91908101906140da565b939a9299509097509550909350915050565b6000610934846116696040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612e1e565b606080825160405190808252806020026020018201604052801561169c578160200160208202803883390190505b50905060005b83518114611490578381815181106116b657fe5b60200260200101516001600160a01b0316318282815181106116d457fe5b60209081029190910101526001016116a2565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$7a69f714cdde1cb6b62e3c39bc8c94deae$__916302cffc459161174d916001600160a01b039091169088908890889060040161489f565b60006040518083038186803b15801561176557600080fd5b505af4158015611779573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109349190810190613e53565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063a6627e9f906117f69086908690600401614a1e565b60006040518083038186803b15801561180e57600080fd5b505af4158015611822573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261184a91908101906142c9565b9392505050565b6006546001600160a01b031681565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063b43cffe1906118b9908890889088908890600401614965565b60006040518083038186803b1580156118d157600080fd5b505af41580156118e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261190d91908101906142c9565b95945050505050565b600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119529190614d49565b60006040518083038186803b15801561196a57600080fd5b505af415801561197e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261067c919081019061422c565b60075481565b6005546001600160a01b031681565b6000806060600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63ca49f47c866040518263ffffffff1660e01b81526004016119f99190614d49565b60006040518083038186803b158015611a1157600080fd5b505af4158015611a25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614184565b606060008251905080604051908082528060200260200182016040528015611a7f578160200160208202803883390190505b50915060005b8181146107d257611a9c8585838151811061142657fe5b838281518110611aa857fe5b6020908102919091010152600101611a85565b600080611ace838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c395760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b3c9190614d49565b60006040518083038186803b158015611b5457600080fd5b505af4158015611b68573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b90919081019061422c565b80519194509250905060005b818114611c2e57838181518110611baf57fe5b602002602001015160001415611bc457611c26565b6000611bd6898584815181106107a657fe5b90506000858381518110611be657fe5b60200260200101518281611bf657fe5b04905080611c0e5760009750505050505050506106cd565b87811080611c1a575087155b15611c23578097505b50505b600101611b9c565b506106cd9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c9a576000611c7e84601063ffffffff612ab916565b600154909150610c0790829087906001600160a01b0316612e2d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fea5760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401611d069190614d49565b60606040518083038186803b158015611d1e57600080fd5b505af4158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d5691908101906141ec565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611da2918a916001600160a01b0390911690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611e1d9190614855565b600060405180830381855afa9150503d8060008114611e58576040519150601f19603f3d011682016040523d82523d6000602084013e611e5d565b606091505b5091509150811580611e7157508051602014155b80611e8d5750611e8881600063ffffffff612be416565b600114155b15611fdb576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611ec8908790602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f3f9190614855565b600060405180830381855afa9150503d8060008114611f7a576040519150601f19603f3d011682016040523d82523d6000602084013e611f7f565b606091505b509093509150828015611f93575081516020145b8015611fc257506002546001600160a01b0316611fb783600c63ffffffff612ab916565b6001600160a01b0316145b611fcd576000611fd0565b60015b60ff169750506110ba565b60001996505050505050611490565b6001600160e01b031981167fa7cb5fb7000000000000000000000000000000000000000000000000000000001415612211576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__90639eadc8359061206f908790600401614d49565b60006040518083038186803b15801561208757600080fd5b505af415801561209b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120c391908101906140da565b5050600354604051929450606093507fe985e9c5000000000000000000000000000000000000000000000000000000009261210d925089916001600160a01b031690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b0316836040516121889190614855565b600060405180830381855afa9150503d80600081146121c3576040519150601f19603f3d011682016040523d82523d6000602084013e6121c8565b606091505b50915091508180156121db575080516020145b80156121f757506121f381600063ffffffff612be416565b6001145b612202576000612206565b6000195b955050505050611490565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561224d576000199150611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156114905760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016122b99190614d49565b60006040518083038186803b1580156122d157600080fd5b505af41580156122e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261230d9190810190614070565b50925092505061231b612bf0565b6001600160a01b0316826001600160a01b031614801561234857506005546001600160a01b038281169116145b1561239257600061236d61235a612c08565b60055489906001600160a01b0316612e2d565b905060001981146123865761238181612c20565b61238a565b6000195b9450506123ae565b6006546001600160a01b03828116911614156123ae5760001993505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91638dccde0491610ad0916001600160a01b0316908890889088906004016149e9565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d3d862d1906117f69086908690600401614b31565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d4695028906124c1908590600401614d49565b60206040518083038186803b1580156124d957600080fd5b505af41580156124ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610881919081019061401e565b60608060606000855190508060405190808252806020026020018201604052801561255657816020015b612543613691565b81526020019060019003908161253b5790505b50935080604051908082528060200260200182016040528015612583578160200160208202803883390190505b509250806040519080825280602002602001820160405280156125b0578160200160208202803883390190505b50915060005b818114612640576125ed8782815181106125cc57fe5b60200260200101518783815181106125e057fe5b6020026020010151612662565b87518890859081106125fb57fe5b6020026020010187858151811061260e57fe5b6020026020010187868151811061262157fe5b93151560209485029190910190930192909252919052526001016125b6565b50509250925092565b6060806126568484611a4d565b91506106a28484610757565b61266a613691565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b9906126b4908890600401614e8e565b60606040518083038186803b1580156126cc57600080fd5b505afa1580156126e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127049190810190614420565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127569089908990600401614ecc565b60206040518083038186803b15801561276e57600080fd5b505afa158015612782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127a69190810190613ffe565b915060006127b387612f37565b905060006127d4886101800151896101400151612f5390919063ffffffff16565b1561280957612802826127f88a60c001518b60800151612f7890919063ffffffff16565b8a60a00151612f94565b9050612876565b60c0880151612825576128028289608001518a60a00151612f94565b6000612836848a61018001516106ab565b9050600061284d848b608001518c60a00151612f94565b90506000612864838c60c001518d60a00151612f94565b905061287082826129ff565b93505050505b61289a61289487604001518a60a00151612fb690919063ffffffff16565b826129ff565b9450600460005460208a01516040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__92639caa023b92612905926001600160a01b03909216918e91908c906004016149e9565b60206040518083038186803b15801561291d57600080fd5b505af4158015612931573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129559190810190614318565b600481111561296057fe5b1461296c57600061296e565b845b945061297e886101600151612fd5565b61298757600094505b60e0880151158015906129a557506129a3886101a00151612fd5565b155b156129af57600094505b6003865160068111156129be57fe5b146129c857600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b6000818310612a0e578161184a565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b600061184a82612a7b8561318a565b613205565b60008160040183511015612aa657612aa6612aa1600385518560040161323f565b6132ae565b5001602001516001600160e01b03191690565b60008160140183511015612ada57612ada612aa1600485518560140161323f565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a082310000000000000000000000000000000000000000000000000000000090612b32908790602401614871565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612b859190614855565b600060405180830381855afa9150503d8060008114612bc0576040519150601f19603f3d011682016040523d82523d6000602084013e612bc5565b606091505b5091509150818015612bd8575080516020145b156107d2576106c88160005b600061184a83836132b6565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612c2b612c08565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c6557600080fd5b505af1158015612c79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c9d91908101906142fc565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612cdc57600080fd5b505af1158015612cf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d149190810190614629565b4211612d9257816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612d5557600080fd5b505af1158015612d69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d8d9190810190614629565b612e05565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612dcd57600080fd5b505af1158015612de1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e059190810190614629565b9050610934816b033b2e3c9fd0803ce800000086612f94565b600061184a82612a7b856132e0565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612e759088908890602401614885565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612ec89190614855565b600060405180830381855afa9150503d8060008114612f03576040519150601f19603f3d011682016040523d82523d6000602084013e612f08565b606091505b5091509150818015612f1b575080516020145b15612f2e57612f2b816000612be4565b92505b50509392505050565b6000806000612f4584613399565b9150915061093482826129ff565b60008151835114801561184a5750508051602091820120825192909101919091201490565b60008282018381101561184a5761184a612aa16000868661359c565b600061093483612faa868563ffffffff6135bb16565b9063ffffffff6135ec16565b600082821115612fcf57612fcf612aa16002858561359c565b50900390565b60006020825181612fe257fe5b06600414612ff257506000610884565b6000613004838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613041576001915050610884565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063bbb2dcf690613094908790600401614d49565b60006040518083038186803b1580156130ac57600080fd5b505af41580156130c0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130e8919081019061422c565b80519093509150600090505b81811461317e576000613124600085848151811061310e57fe5b6020026020010151612a8090919063ffffffff16565b90506001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415613175576131628483613616565b1561317557600095505050505050610884565b506001016130f4565b50600195945050505050565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b84848460405160240161325e93929190614dbc565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156132d7576132d7612aa1600585518560200161323f565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561332c57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b60008060048361014001515110156133b657506000905080611525565b6101408301516000906133cf908263ffffffff612a8016565b90506001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561356c576101408401516040517f750bdb3000000000000000000000000000000000000000000000000000000000815260009173__$d8b635de2bf4a097b4e18b67d0fb68e779$__9163750bdb309161345a91600401614d49565b60006040518083038186803b15801561347257600080fd5b505af4158015613486573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134ae9190810190614070565b506006549093506001600160a01b0380851691161415915061356a90505773__$ef9cb1cf4426222cc0af8204def2680bac$__63d12a7960866134ef613679565b6040518363ffffffff1660e01b815260040161350c929190614ea1565b60206040518083038186803b15801561352457600080fd5b505af4158015613538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061355c9190810190614629565b600019935093505050611525565b505b61357f8460000151856101400151610ba1565b6135928560000151866101400151611abb565b9250925050915091565b606063e946c1bb60e01b84848460405160240161325e93929190614d9a565b6000826135ca575060006106cd565b828202828482816135d757fe5b041461184a5761184a612aa16001868661359c565b60008161360257613602612aa16003858561359c565b600082848161360d57fe5b04949350505050565b8151600090600183015b818110156107d25761366185828151811061363757fe5b602002602001015186868151811061364b57fe5b6020026020010151612f5390919063ffffffff16565b15613671576001925050506106cd565b600101613620565b731e0447b19bb6ecfdae1e4ae1694b0c3659614e4e90565b6040805160608101909152806000815260006020820181905260409091015290565b80356106cd81614fa4565b80516106cd81614fa4565b600082601f8301126136d9578081fd5b81356136ec6136e782614f26565b614eff565b81815291506020808301908481018184028601820187101561370d57600080fd5b60005b8481101561148b57813561372381614fa4565b84529282019290820190600101613710565b600082601f830112613745578081fd5b81356137536136e782614f26565b8181529150602080830190840160005b838110156137905761377b8760208435890101613924565b83526020928301929190910190600101613763565b5050505092915050565b600082601f8301126137aa578081fd5b81516137b86136e782614f26565b8181529150602080830190840160005b83811015613790576137e08760208451890101613972565b835260209283019291909101906001016137c8565b600082601f830112613805578081fd5b81356138136136e782614f26565b8181529150602080830190840160005b838110156137905761383b87602084358901016139c7565b83526020928301929190910190600101613823565b600082601f830112613860578081fd5b813561386e6136e782614f26565b81815291506020808301908481018184028601820187101561388f57600080fd5b60005b8481101561148b57813584529282019290820190600101613892565b600082601f8301126138be578081fd5b81516138cc6136e782614f26565b8181529150602080830190848101818402860182018710156138ed57600080fd5b60005b8481101561148b578151845292820192908201906001016138f0565b80516001600160e01b0319811681146106cd57600080fd5b600082601f830112613934578081fd5b81356139426136e782614f46565b915080825283602082850101111561395957600080fd5b8060208401602084013760009082016020015292915050565b600082601f830112613982578081fd5b81516139906136e782614f46565b91508082528360208285010111156139a757600080fd5b611490816020840160208601614f6a565b8051600581106106cd57600080fd5b60006101c08083850312156139da578182fd5b6139e381614eff565b9150506139f083836136b3565b81526139ff83602084016136b3565b6020820152613a1183604084016136b3565b6040820152613a2383606084016136b3565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115613a8557600080fd5b613a9186838701613924565b83850152610160925082850135915080821115613aad57600080fd5b613ab986838701613924565b83850152610180925082850135915080821115613ad557600080fd5b613ae186838701613924565b838501526101a0925082850135915080821115613afd57600080fd5b50613b0a85828601613924565b82840152505092915050565b60006101c0808385031215613b29578182fd5b613b3281614eff565b915050613b3f83836136be565b8152613b4e83602084016136be565b6020820152613b6083604084016136be565b6040820152613b7283606084016136be565b60608201526080820151608082015260a082015160a082015260c082015160c082015260e082015160e08201526101008083015181830152506101208083015181830152506101408083015167ffffffffffffffff80821115613bd457600080fd5b613be086838701613972565b83850152610160925082850151915080821115613bfc57600080fd5b613c0886838701613972565b83850152610180925082850151915080821115613c2457600080fd5b613c3086838701613972565b838501526101a0925082850151915080821115613c4c57600080fd5b50613b0a85828601613972565b600060208284031215613c6a578081fd5b813561184a81614fa4565b60008060408385031215613c87578081fd5b8235613c9281614fa4565b9150602083013567ffffffffffffffff811115613cad578182fd5b613cb985828601613735565b9150509250929050565b60008060008060808587031215613cd8578182fd5b8435613ce381614fa4565b9350602085013567ffffffffffffffff80821115613cff578384fd5b613d0b88838901613850565b94506040870135915080821115613d20578384fd5b613d2c88838901613850565b93506060870135915080821115613d41578283fd5b50613d4e87828801613924565b91505092959194509250565b60008060408385031215613d6c578182fd5b8235613d7781614fa4565b9150602083013567ffffffffffffffff811115613d92578182fd5b613cb985828601613924565b600080600060608486031215613db2578081fd5b8335613dbd81614fa4565b9250602084013567ffffffffffffffff811115613dd8578182fd5b613de486828701613924565b925050604084013590509250925092565b60008060408385031215613e07578182fd5b8235613e1281614fa4565b946020939093013593505050565b600060208284031215613e31578081fd5b813567ffffffffffffffff811115613e47578182fd5b610934848285016136c9565b60006020808385031215613e65578182fd5b825167ffffffffffffffff811115613e7b578283fd5b80840185601f820112613e8c578384fd5b80519150613e9c6136e783614f26565b8281528381019082850185850284018601891015613eb8578687fd5b8693505b84841015613ee257613ece89826139b8565b835260019390930192918501918501613ebc565b50979650505050505050565b600080600060608486031215613f02578081fd5b833567ffffffffffffffff80821115613f19578283fd5b613f25878388016137f5565b94506020860135915080821115613f3a578283fd5b613f46878388016136c9565b93506040860135915080821115613f5b578283fd5b50613f6886828701613850565b9150509250925092565b60008060408385031215613f84578182fd5b823567ffffffffffffffff80821115613f9b578384fd5b613fa7868387016137f5565b93506020850135915080821115613fbc578283fd5b50613cb985828601613735565b60008060408385031215613fdb578182fd5b823567ffffffffffffffff80821115613ff2578384fd5b613fa786838701613850565b60006020828403121561400f578081fd5b8151801515811461184a578182fd5b60006020828403121561402f578081fd5b61184a838361390c565b6000806040838503121561404b578182fd5b614055848461390c565b9150602083015161406581614fa4565b809150509250929050565b60008060008060808587031215614085578182fd5b845161409081614fb9565b60208601519094506140a181614fa4565b60408601519093506140b281614fa4565b606086015190925067ffffffffffffffff8111156140ce578182fd5b613d4e87828801613972565b600080600080600060a086880312156140f1578283fd5b6140fb878761390c565b9450602086015161410b81614fa4565b604087015190945067ffffffffffffffff80821115614128578485fd5b61413489838a016138ae565b94506060880151915080821115614149578283fd5b61415589838a016138ae565b9350608088015191508082111561416a578283fd5b5061417788828901613972565b9150509295509295909350565b60008060008060808587031215614199578182fd5b6141a3868661390c565b935060208501516141b381614fa4565b604086015190935067ffffffffffffffff8111156141cf578283fd5b6141db87828801613972565b606096909601519497939650505050565b600080600060608486031215614200578081fd5b61420a858561390c565b9250602084015161421a81614fa4565b80925050604084015190509250925092565b600080600060608486031215614240578081fd5b835161424b81614fb9565b602085015190935067ffffffffffffffff80821115614268578283fd5b614274878388016138ae565b93506040860151915080821115614289578283fd5b50613f688682870161379a565b6000602082840312156142a7578081fd5b813567ffffffffffffffff8111156142bd578182fd5b61093484828501613924565b6000602082840312156142da578081fd5b815167ffffffffffffffff8111156142f0578182fd5b61093484828501613972565b60006020828403121561430d578081fd5b815161184a81614fa4565b600060208284031215614329578081fd5b61184a83836139b8565b60008060008060808587031215614348578182fd5b845167ffffffffffffffff8082111561435f578384fd5b61436b88838901613972565b9550602091508187015181811115614381578485fd5b80880189601f820112614392578586fd5b805191506143a26136e783614f26565b82815284810190828601885b858110156143d7576143c58e898451880101613b16565b845292870192908701906001016143ae565b505060408b01519098509450505050808211156143f2578384fd5b6143fe888389016138ae565b93506060870151915080821115614413578283fd5b50613d4e8782880161379a565b600060608284031215614431578081fd5b61443b6060614eff565b825160078110614449578283fd5b8152602083810151908201526040928301519281019290925250919050565b60008060006060848603121561447c578081fd5b833567ffffffffffffffff811115614492578182fd5b61449e868287016139c7565b93505060208401356144af81614fa4565b929592945050506040919091013590565b600080604083850312156144d2578182fd5b823567ffffffffffffffff808211156144e9578384fd5b6144f5868387016139c7565b9350602085013591508082111561450a578283fd5b50613cb985828601613924565b60008060006060848603121561452b578081fd5b833567ffffffffffffffff811115614541578182fd5b61454d868287016139c7565b93505060208401359150604084013561456581614fa4565b809150509250925092565b600080600060608486031215614584578081fd5b833567ffffffffffffffff8082111561459b578283fd5b81860160a081890312156145ad578384fd5b6145b760a0614eff565b925080358352602081013560208401526040810135604084015260608101356145df81614fa4565b60608401526080810135828111156145f5578485fd5b61460189828401613924565b60808501525091945050506020840135915061462085604086016136b3565b90509250925092565b60006020828403121561463a578081fd5b5051919050565b6001600160a01b0316815260200190565b600061465e8383614731565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b848110156146be5782840388526146a8848351614705565b6020988901989094509190910190600101614690565b50919695505050505050565b6000815180845260208401935060208301825b828110156146fb5781518652602095860195909101906001016146dd565b5093949350505050565b6000815180845261471d816020860160208601614f6a565b601f01601f19169290920160200192915050565b80516007811061473d57fe5b825260208181015190830152604090810151910152565b60006101c0614764848451614666565b60208301516147766020860182614666565b5060408301516147896040860182614666565b50606083015161479c6060860182614666565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e08501526101008084015181860152506101208084015181860152506101408084015182828701526147f583870182614705565b915050610160915081840151858203838701526148128282614705565b92505050610180808401518583038287015261482e8382614705565b9150506101a09150818401518582038387015261484b8282614705565b9695505050505050565b60008251614867818460208701614f6a565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516148c68185614bc6565b91508193508281028201838a01865b838110156148ff5786830385526148ed838351614754565b948601949250908501906001016148d5565b505086810360408801528094508851925061491a8382614bc6565b94505050818701845b8281101561494457614936858351614641565b945090830190600101614923565b50505050828103606084015261495a81856146ca565b979650505050505050565b60006001600160a01b03861682526080602083015261498760808301866146ca565b828103604084015261499981866146ca565b83810360608501526149ab8186614705565b98975050505050505050565b60006001600160a01b0385168252606060208301526149d96060830185614705565b9050826040830152949350505050565b60006001600160a01b03808716835260806020840152614a0c6080840187614754565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b81811015614a78578351614a6481614f9a565b835260209384019390920191600101614a51565b509095945050505050565b60006060820160608352808651614a9a8184614bc6565b915060209250828801845b82811015614ac657614ab8848351614652565b935090840190600101614aa5565b50505083810382850152614ada81876146ca565b84810360408601528551808252908301915082860190845b81811015614b10578251151584529284019291840191600101614af2565b509198975050505050505050565b60006020825261184a60208301846146ca565b600060408252614b4460408301856146ca565b602083820381850152818551808452828401915082838202850101838801865b83811015614b9257601f19878403018552614b80838351614705565b94860194925090850190600101614b64565b50909998505050505050505050565b600060408252614bb460408301856146ca565b828103602084015261190d81856146ca565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b0380861660208401528085166040840152506080606083015261484b6080830184614705565b60006001600160e01b0319871682526001600160a01b038616602083015260a06040830152614c7560a08301866146ca565b8281036060840152614c8781866146ca565b8381036080850152614c998186614705565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614cd86080830185614705565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614d3760608301856146ca565b828103604084015261484b8185614673565b60006020825261184a6020830184614705565b600060808252614d6f6080830187614705565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614da857fe5b938152602081019290925260409091015290565b6060810160088510614da857fe5b60208101614dd783614f9a565b91905290565b600060808252614df06080830187614705565b602083820381850152818751808452828401915082838202850101838a01865b83811015614e3e57601f19878403018552614e2c838351614754565b94860194925090850190600101614e10565b50508681036040880152614e52818a6146ca565b945050505050828103606084015261495a8185614673565b60a08101614e788286614731565b8360608301528215156080830152949350505050565b60006020825261184a6020830184614754565b600060408252614eb46040830185614754565b90506001600160a01b03831660208301529392505050565b600060408252614edf6040830185614754565b828103602084015261190d8185614705565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614f1e57600080fd5b604052919050565b600067ffffffffffffffff821115614f3c578081fd5b5060209081020190565b600067ffffffffffffffff821115614f5c578081fd5b50601f01601f191660200190565b60005b83811015614f85578181015183820152602001614f6d565b83811115614f94576000848401525b50505050565b6005811061075457fe5b6001600160a01b038116811461075457600080fd5b6001600160e01b03198116811461075457600080fdfea365627a7a72315820f05bfff91cf6f387ce89e4ca5ccc415a4c291e39b349e6d1c630a5574b2fee306c6578706572696d656e74616cf564736f6c63430005100040" } } }, diff --git a/packages/contract-wrappers/src/generated-wrappers/dev_utils.ts b/packages/contract-wrappers/src/generated-wrappers/dev_utils.ts index e1618f6419..af83160db5 100644 --- a/packages/contract-wrappers/src/generated-wrappers/dev_utils.ts +++ b/packages/contract-wrappers/src/generated-wrappers/dev_utils.ts @@ -42,7 +42,7 @@ export class DevUtilsContract extends BaseContract { * @ignore */ public static deployedBytecode = - '0x608060405234801561001057600080fd5b506004361061025c5760003560e01c8063a5cd62ba11610145578063d3637905116100bd578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105b5578063ef3bb097146105bd578063ff84e7cc146105c55761025c565b8063e4e6e7da14610572578063e77286eb146105935761025c565b8063d36379051461050a578063d3d862d11461051d578063d469502814610530578063e25cabf7146105505761025c565b8063c26cfecd11610114578063ca49f47c116100f9578063ca49f47c146104c1578063d001c5dc146104e4578063d186037f146104f75761025c565b8063c26cfecd146104b1578063c82037ef146104b95761025c565b8063a5cd62ba14610449578063a6627e9f14610469578063b43cffe11461047c578063bbb2dcf61461048f5761025c565b80637982653e116101d85780639baf2705116101a75780639eadc8351161018c5780639eadc835146103ff578063a070cac814610423578063a0901e51146104365761025c565b80639baf2705146103e25780639cd01605146103f75761025c565b80637982653e1461037b57806379c9c4261461039b5780637d727512146103ae5780638f4ce479146103c15761025c565b80634dfdac201161022f57806363eb39921161021457806363eb3992146103225780636f83188e14610335578063750bdb30146103585761025c565b80634dfdac20146102e2578063590aa875146103025761025c565b806304a5618a146102615780630d7b7d761461028c5780632322cf76146102ad57806346eb65cb146102cd575b600080fd5b61027461026f366004613e8f565b6105cd565b60405161028393929190614a5a565b60405180910390f35b61029f61029a366004613953565b610666565b604051610283929190614c37565b6102c06102bb366004613953565b610688565b6040516102839190614937565b6102e06102db366004613e8f565b6106b0565b005b6102f56102f036600461386e565b610734565b604051610283919061488f565b610315610310366004613852565b6107b7565b6040516102839190614aba565b610315610330366004613997565b610866565b610348610343366004613e8f565b610919565b6040516102839493929190614b4e565b61036b610366366004613e8f565b6109b9565b6040516102839493929190614978565b61038e6103893660046141da565b610a4b565b6040516102839190614b3b565b6102c06103a93660046142e2565b610afd565b6102c06103bc366004613953565b610b7e565b6103d46103cf366004613e8f565b611474565b604051610283929190614955565b6103ea611507565b60405161028391906145e2565b6103ea611516565b61041261040d366004613e8f565b611525565b6040516102839594939291906149b4565b6102c0610431366004614289565b6115ca565b6102f5610444366004613a19565b61164b565b61045c610457366004613ae7565b6116c4565b60405161028391906147a8565b6103156104773660046139ee565b61177e565b61031561048a3660046138bc565b61182e565b6104a261049d366004613e8f565b6118e4565b60405161028393929190614a85565b6102c0611974565b6103ea61197a565b6104d46104cf366004613e8f565b611989565b6040516102839493929190614a17565b6102f56104f236600461386e565b611a1b565b6102c0610505366004613953565b611a89565b61038e6105183660046141da565b612365565b61031561052b366004613bc2565b6123c7565b61054361053e366004613e8f565b61241c565b6040516102839190614940565b61056361055e366004613b6b565b6124bf565b604051610283939291906147f4565b61058561058036600461386e565b6125f7565b604051610283929190614912565b6105a66105a1366004614232565b612610565b60405161028393929190614bdb565b6103ea61287f565b6103ea61288e565b6103ea61289d565b600080600073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a856040518263ffffffff1660e01b81526004016106099190614aba565b60606040518083038186803b15801561062157600080fd5b505af4158015610635573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106599190810190613de5565b9250925092509193909250565b6000806106738484610b7e565b915061067f8484611a89565b90509250929050565b60008060006106978585610666565b915091506106a582826128ac565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$dca6deb482199a44a74cce8be70bfdd753$__906346eb65cb90610700908490600401614aba565b60006040518083038186803b15801561071857600080fd5b505af415801561072c573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610766578160200160208202803883390190505b50915060005b8181146107af576107908585838151811061078357fe5b6020026020010151611a89565b83828151811061079c57fe5b602090810291909101015260010161076c565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063590aa8759061080a9085906004016145e2565b60006040518083038186803b15801561082257600080fd5b505af4158015610836573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261085e9190810190613ec2565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__906363eb3992906108bd90879087908790600401614728565b60006040518083038186803b1580156108d557600080fd5b505af41580156108e9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613ec2565b949350505050565b60608060608073__$d88c3928727cb33b5732520dfb08856c25$__636f83188e866040518263ffffffff1660e01b81526004016109569190614aba565b60006040518083038186803b15801561096e57600080fd5b505af4158015610982573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613f30565b93509350935093509193509193565b6000806000606073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016109f79190614aba565b60006040518083038186803b158015610a0f57600080fd5b505af4158015610a23573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613c69565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91639caa023b91610aad916001600160a01b03169088908890889060040161475a565b60206040518083038186803b158015610ac557600080fd5b505af4158015610ad9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109119190810190613f11565b600061091184610b796040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612919565b600080610b91838263ffffffff61292d16565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610bec576000610bd884601063ffffffff61296616565b9050610be48186612999565b92505061146d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e055760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401610c589190614aba565b60606040518083038186803b158015610c7057600080fd5b505af4158015610c84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ca89190810190613de5565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610ce4908490602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d5f91906145c6565b600060405180830381855afa9150503d8060008114610d9a576040519150601f19603f3d011682016040523d82523d6000602084013e610d9f565b606091505b50915091506000828015610db4575081516020145b610dbf576000610dd0565b610dd082600c63ffffffff61296616565b9050896001600160a01b0316816001600160a01b031614610df2576000610df5565b60015b60ff16975050505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110a157600060608073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b8152600401610e739190614aba565b60006040518083038186803b158015610e8b57600080fd5b505af4158015610e9f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ec79190810190613cd3565b5081519296509094509250905060005b81811461109757828181518110610eea57fe5b602002602001015160001415610eff5761108f565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f3357fe5b6020026020010151604051602401610f4c92919061478f565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fc791906145c6565b600060405180830381855afa9150503d8060008114611002576040519150601f19603f3d011682016040523d82523d6000602084013e611007565b606091505b5091509150600082801561101c575081516020145b611027576000611038565b61103882600063ffffffff612a9116565b9050600087868151811061104857fe5b6020026020010151828161105857fe5b049050806110745760009b5050505050505050505050506106aa565b8b81108061108057508b155b1561108957809b505b50505050505b600101610ed7565b505050505061146d565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111d4576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061111090869060009081908190602401614acd565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b0316906111779084906145c6565b600060405180830381855afa9150503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b50509050806111c75760006111cb565b6000195b9350505061146d565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156112fd5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016112409190614aba565b60006040518083038186803b15801561125857600080fd5b505af415801561126c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112949190810190613c69565b5092509250506112a2612a9d565b6001600160a01b0316826001600160a01b03161480156112cf57506005546001600160a01b038281169116145b156112f65760006112e76112e1612ab5565b88612999565b90506112f281612acd565b9450505b505061146d565b6001600160e01b031981167f94cfcdd700000000000000000000000000000000000000000000000000000000141561146d5760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b81526004016113699190614aba565b60006040518083038186803b15801561138157600080fd5b505af4158015611395573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113bd9190810190613e25565b80519194509250905060005b818114611468578381815181106113dc57fe5b6020026020010151600014156113f157611460565b60006114108985848151811061140357fe5b6020026020010151610b7e565b9050600085838151811061142057fe5b6020026020010151828161143057fe5b049050806114485760009750505050505050506106aa565b87811080611454575087155b1561145d578097505b50505b6001016113c9565b505050505b5092915050565b60008073__$dca6deb482199a44a74cce8be70bfdd753$__638f4ce479846040518263ffffffff1660e01b81526004016114ae9190614aba565b604080518083038186803b1580156114c557600080fd5b505af41580156114d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114fd9190810190613c32565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$dca6deb482199a44a74cce8be70bfdd753$__639eadc835876040518263ffffffff1660e01b81526004016115649190614aba565b60006040518083038186803b15801561157c57600080fd5b505af4158015611590573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115b89190810190613cd3565b939a9299509097509550909350915050565b6000610911846116466040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e3000000000000000000000000000000000000000000000000000000081525087876128c2565b612ccb565b6060808251604051908082528060200260200182016040528015611679578160200160208202803883390190505b50905060005b8351811461146d5783818151811061169357fe5b60200260200101516001600160a01b0316318282815181106116b157fe5b602090810291909101015260010161167f565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$0c5fffa094d709a948ceda4c4b1013b697$__916302cffc459161172a916001600160a01b0390911690889088908890600401614610565b60006040518083038186803b15801561174257600080fd5b505af4158015611756573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109119190810190613a4c565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063a6627e9f906117d3908690869060040161478f565b60006040518083038186803b1580156117eb57600080fd5b505af41580156117ff573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118279190810190613ec2565b9392505050565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063b43cffe1906118879088908890889088906004016146d6565b60006040518083038186803b15801561189f57600080fd5b505af41580156118b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118db9190810190613ec2565b95945050505050565b600060608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119209190614aba565b60006040518083038186803b15801561193857600080fd5b505af415801561194c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106599190810190613e25565b60065481565b6005546001600160a01b031681565b6000806060600073__$dca6deb482199a44a74cce8be70bfdd753$__63ca49f47c866040518263ffffffff1660e01b81526004016119c79190614aba565b60006040518083038186803b1580156119df57600080fd5b505af41580156119f3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109aa9190810190613d7d565b606060008251905080604051908082528060200260200182016040528015611a4d578160200160208202803883390190505b50915060005b8181146107af57611a6a8585838151811061140357fe5b838281518110611a7657fe5b6020908102919091010152600101611a53565b600080611a9c838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c075760608073__$dca6deb482199a44a74cce8be70bfdd753$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b0a9190614aba565b60006040518083038186803b158015611b2257600080fd5b505af4158015611b36573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b5e9190810190613e25565b80519194509250905060005b818114611bfc57838181518110611b7d57fe5b602002602001015160001415611b9257611bf4565b6000611ba48985848151811061078357fe5b90506000858381518110611bb457fe5b60200260200101518281611bc457fe5b04905080611bdc5760009750505050505050506106aa565b87811080611be8575087155b15611bf1578097505b50505b600101611b6a565b506106aa9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c68576000611c4c84601063ffffffff61296616565b600154909150610be490829087906001600160a01b0316612cda565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fb85760008073__$dca6deb482199a44a74cce8be70bfdd753$__6304a5618a866040518263ffffffff1660e01b8152600401611cd49190614aba565b60606040518083038186803b158015611cec57600080fd5b505af4158015611d00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d249190810190613de5565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611d70918a916001600160a01b03909116906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611deb91906145c6565b600060405180830381855afa9150503d8060008114611e26576040519150601f19603f3d011682016040523d82523d6000602084013e611e2b565b606091505b5091509150811580611e3f57508051602014155b80611e5b5750611e5681600063ffffffff612a9116565b600114155b15611fa9576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611e96908790602401614937565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f0d91906145c6565b600060405180830381855afa9150503d8060008114611f48576040519150601f19603f3d011682016040523d82523d6000602084013e611f4d565b606091505b509093509150828015611f61575081516020145b8015611f9057506002546001600160a01b0316611f8583600c63ffffffff61296616565b6001600160a01b0316145b611f9b576000611f9e565b60015b60ff16975050611097565b6000199650505050505061146d565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156121df576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__90639eadc8359061203d908790600401614aba565b60006040518083038186803b15801561205557600080fd5b505af4158015612069573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120919190810190613cd3565b5050600354604051929450606093507fe985e9c500000000000000000000000000000000000000000000000000000000926120db925089916001600160a01b0316906024016145f6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b03168360405161215691906145c6565b600060405180830381855afa9150503d8060008114612191576040519150601f19603f3d011682016040523d82523d6000602084013e612196565b606091505b50915091508180156121a9575080516020145b80156121c557506121c181600063ffffffff612a9116565b6001145b6121d05760006121d4565b6000195b95505050505061146d565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561221b57600019915061146d565b6001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561146d5760008073__$dca6deb482199a44a74cce8be70bfdd753$__63750bdb30866040518263ffffffff1660e01b81526004016122879190614aba565b60006040518083038186803b15801561229f57600080fd5b505af41580156122b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122db9190810190613c69565b5092509250506122e9612a9d565b6001600160a01b0316826001600160a01b031614801561231657506005546001600160a01b038281169116145b1561235c57600061233b612328612ab5565b60055489906001600160a01b0316612cda565b905060001981146123545761234f81612acd565b612358565b6000195b9450505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$0c5fffa094d709a948ceda4c4b1013b697$__91638dccde0491610aad916001600160a01b03169088908890889060040161475a565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063d3d862d1906117d390869086906004016148a2565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$dca6deb482199a44a74cce8be70bfdd753$__9063d46950289061246f908590600401614aba565b60206040518083038186803b15801561248757600080fd5b505af415801561249b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061085e9190810190613c17565b60608060606000855190508060405190808252806020026020018201604052801561250457816020015b6124f16133cd565b8152602001906001900390816124e95790505b50935080604051908082528060200260200182016040528015612531578160200160208202803883390190505b5092508060405190808252806020026020018201604052801561255e578160200160208202803883390190505b50915060005b8181146125ee5761259b87828151811061257a57fe5b602002602001015187838151811061258e57fe5b6020026020010151612610565b87518890859081106125a957fe5b602002602001018785815181106125bc57fe5b602002602001018786815181106125cf57fe5b9315156020948502919091019093019290925291905252600101612564565b50509250925092565b6060806126048484611a1b565b915061067f8484610734565b6126186133cd565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b990612662908890600401614bff565b60606040518083038186803b15801561267a57600080fd5b505afa15801561268e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126b29190810190614192565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127049089908990600401614c12565b60206040518083038186803b15801561271c57600080fd5b505afa158015612730573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127549190810190613bf7565b9150600061276187612de4565b90506000612782886101800151896101400151612e0090919063ffffffff16565b156127b7576127b0826127a68a60c001518b60800151612e2590919063ffffffff16565b8a60a00151612e41565b9050612824565b60c08801516127d3576127b08289608001518a60a00151612e41565b60006127e4848a6101800151610688565b905060006127fb848b608001518c60a00151612e41565b90506000612812838c60c001518d60a00151612e41565b905061281e82826128ac565b93505050505b61284861284287604001518a60a00151612e6390919063ffffffff16565b826128ac565b945061285388612e82565b61285c57600094505b60038651600681111561286b57fe5b1461287557600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b60008183106128bb5781611827565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b60006118278261292885612ee9565b612f64565b600081600401835110156129535761295361294e6003855185600401612f9e565b61300d565b5001602001516001600160e01b03191690565b600081601401835110156129875761298761294e6004855185601401612f9e565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a0823100000000000000000000000000000000000000000000000000000000906129df9087906024016145e2565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612a3291906145c6565b600060405180830381855afa9150503d8060008114612a6d576040519150601f19603f3d011682016040523d82523d6000602084013e612a72565b606091505b5091509150818015612a85575080516020145b156107af576106a58160005b60006118278383613015565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612ad8612ab5565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b1257600080fd5b505af1158015612b26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b4a9190810190613ef5565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b8957600080fd5b505af1158015612b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bc1919081019061439a565b4211612c3f57816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c3a919081019061439a565b612cb2565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c7a57600080fd5b505af1158015612c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cb2919081019061439a565b9050610911816b033b2e3c9fd0803ce800000086612e41565b6000611827826129288561303f565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612d2290889088906024016145f6565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612d7591906145c6565b600060405180830381855afa9150503d8060008114612db0576040519150601f19603f3d011682016040523d82523d6000602084013e612db5565b606091505b5091509150818015612dc8575080516020145b15612ddb57612dd8816000612a91565b92505b50509392505050565b6000806000612df2846130f8565b9150915061091182826128ac565b6000815183511480156118275750508051602091820120825192909101919091201490565b6000828201838110156118275761182761294e6000868661313b565b600061091183612e57868563ffffffff61315a16565b9063ffffffff61318b16565b600082821115612e7c57612e7c61294e6002858561313b565b50900390565b6000612e928261014001516131b5565b8015612eb3575060c08201511580612eb35750612eb38261018001516131b5565b8015612ec85750612ec88261016001516131b5565b801561085e575060e0820151158061085e575061085e826101a001516131b5565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b848484604051602401612fbd93929190614b2d565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156130365761303661294e6005855185602001612f9e565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561308b57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b600080600483610140015151101561311557506000905080611502565b6131288360000151846101400151610b7e565b6114fd8460000151856101400151611a89565b606063e946c1bb60e01b848484604051602401612fbd93929190614b0b565b600082613169575060006106aa565b8282028284828161317657fe5b04146118275761182761294e6001868661313b565b6000816131a1576131a161294e6003858561313b565b60008284816131ac57fe5b04949350505050565b600060208251816131c257fe5b066004146131d257506000610861565b60006131e4838263ffffffff61292d16565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613221576001915050610861565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$dca6deb482199a44a74cce8be70bfdd753$__9063bbb2dcf690613274908790600401614aba565b60006040518083038186803b15801561328c57600080fd5b505af41580156132a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132c89190810190613e25565b80519093509150600090505b81811461335e57600061330460008584815181106132ee57fe5b602002602001015161292d90919063ffffffff16565b90506001600160e01b031981167f0257179200000000000000000000000000000000000000000000000000000000141561335557613342848361336a565b1561335557600095505050505050610861565b506001016132d4565b50600195945050505050565b8151600090600183015b818110156107af576133b585828151811061338b57fe5b602002602001015186868151811061339f57fe5b6020026020010151612e0090919063ffffffff16565b156133c5576001925050506106aa565b600101613374565b6040805160608101909152806000815260006020820181905260409091015290565b80356106aa81614cea565b80516106aa81614cea565b600082601f830112613415578081fd5b813561342861342382614c6c565b614c45565b81815291506020808301908481018184028601820187101561344957600080fd5b60005b8481101561146857813561345f81614cea565b8452928201929082019060010161344c565b600082601f830112613481578081fd5b813561348f61342382614c6c565b8181529150602080830190840160005b838110156134cc576134b78760208435890101613660565b8352602092830192919091019060010161349f565b5050505092915050565b600082601f8301126134e6578081fd5b81516134f461342382614c6c565b8181529150602080830190840160005b838110156134cc5761351c87602084518901016136ae565b83526020928301929190910190600101613504565b600082601f830112613541578081fd5b813561354f61342382614c6c565b8181529150602080830190840160005b838110156134cc576135778760208435890101613703565b8352602092830192919091019060010161355f565b600082601f83011261359c578081fd5b81356135aa61342382614c6c565b8181529150602080830190848101818402860182018710156135cb57600080fd5b60005b84811015611468578135845292820192908201906001016135ce565b600082601f8301126135fa578081fd5b815161360861342382614c6c565b81815291506020808301908481018184028601820187101561362957600080fd5b60005b848110156114685781518452928201929082019060010161362c565b80516001600160e01b0319811681146106aa57600080fd5b600082601f830112613670578081fd5b813561367e61342382614c8c565b915080825283602082850101111561369557600080fd5b8060208401602084013760009082016020015292915050565b600082601f8301126136be578081fd5b81516136cc61342382614c8c565b91508082528360208285010111156136e357600080fd5b61146d816020840160208601614cb0565b8051600581106106aa57600080fd5b60006101c0808385031215613716578182fd5b61371f81614c45565b91505061372c83836133ef565b815261373b83602084016133ef565b602082015261374d83604084016133ef565b604082015261375f83606084016133ef565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156137c157600080fd5b6137cd86838701613660565b838501526101609250828501359150808211156137e957600080fd5b6137f586838701613660565b8385015261018092508285013591508082111561381157600080fd5b61381d86838701613660565b838501526101a092508285013591508082111561383957600080fd5b5061384685828601613660565b82840152505092915050565b600060208284031215613863578081fd5b813561182781614cea565b60008060408385031215613880578081fd5b823561388b81614cea565b9150602083013567ffffffffffffffff8111156138a6578182fd5b6138b285828601613471565b9150509250929050565b600080600080608085870312156138d1578182fd5b84356138dc81614cea565b9350602085013567ffffffffffffffff808211156138f8578384fd5b6139048883890161358c565b94506040870135915080821115613919578384fd5b6139258883890161358c565b9350606087013591508082111561393a578283fd5b5061394787828801613660565b91505092959194509250565b60008060408385031215613965578182fd5b823561397081614cea565b9150602083013567ffffffffffffffff81111561398b578182fd5b6138b285828601613660565b6000806000606084860312156139ab578081fd5b83356139b681614cea565b9250602084013567ffffffffffffffff8111156139d1578182fd5b6139dd86828701613660565b925050604084013590509250925092565b60008060408385031215613a00578182fd5b8235613a0b81614cea565b946020939093013593505050565b600060208284031215613a2a578081fd5b813567ffffffffffffffff811115613a40578182fd5b61091184828501613405565b60006020808385031215613a5e578182fd5b825167ffffffffffffffff811115613a74578283fd5b80840185601f820112613a85578384fd5b80519150613a9561342383614c6c565b8281528381019082850185850284018601891015613ab1578687fd5b8693505b84841015613adb57613ac789826136f4565b835260019390930192918501918501613ab5565b50979650505050505050565b600080600060608486031215613afb578081fd5b833567ffffffffffffffff80821115613b12578283fd5b613b1e87838801613531565b94506020860135915080821115613b33578283fd5b613b3f87838801613405565b93506040860135915080821115613b54578283fd5b50613b618682870161358c565b9150509250925092565b60008060408385031215613b7d578182fd5b823567ffffffffffffffff80821115613b94578384fd5b613ba086838701613531565b93506020850135915080821115613bb5578283fd5b506138b285828601613471565b60008060408385031215613bd4578182fd5b823567ffffffffffffffff80821115613beb578384fd5b613ba08683870161358c565b600060208284031215613c08578081fd5b81518015158114611827578182fd5b600060208284031215613c28578081fd5b6118278383613648565b60008060408385031215613c44578182fd5b613c4e8484613648565b91506020830151613c5e81614cea565b809150509250929050565b60008060008060808587031215613c7e578182fd5b8451613c8981614cff565b6020860151909450613c9a81614cea565b6040860151909350613cab81614cea565b606086015190925067ffffffffffffffff811115613cc7578182fd5b613947878288016136ae565b600080600080600060a08688031215613cea578283fd5b613cf48787613648565b94506020860151613d0481614cea565b604087015190945067ffffffffffffffff80821115613d21578485fd5b613d2d89838a016135ea565b94506060880151915080821115613d42578283fd5b613d4e89838a016135ea565b93506080880151915080821115613d63578283fd5b50613d70888289016136ae565b9150509295509295909350565b60008060008060808587031215613d92578182fd5b613d9c8686613648565b93506020850151613dac81614cea565b604086015190935067ffffffffffffffff811115613dc8578283fd5b613dd4878288016136ae565b606096909601519497939650505050565b600080600060608486031215613df9578081fd5b613e038585613648565b92506020840151613e1381614cea565b80925050604084015190509250925092565b600080600060608486031215613e39578081fd5b8351613e4481614cff565b602085015190935067ffffffffffffffff80821115613e61578283fd5b613e6d878388016135ea565b93506040860151915080821115613e82578283fd5b50613b61868287016134d6565b600060208284031215613ea0578081fd5b813567ffffffffffffffff811115613eb6578182fd5b61091184828501613660565b600060208284031215613ed3578081fd5b815167ffffffffffffffff811115613ee9578182fd5b610911848285016136ae565b600060208284031215613f06578081fd5b815161182781614cea565b600060208284031215613f22578081fd5b815160058110611827578182fd5b60008060008060808587031215613f45578182fd5b845167ffffffffffffffff811115613f5b578283fd5b613f67878288016136ae565b945050602085015167ffffffffffffffff811115613f83578283fd5b86601f8288010112613f93578283fd5b80860151613fa361342382614c6c565b8181526020808201919089850101865b8481101561413c578151868c01016101c0601f19828f03011215613fd5578889fd5b613fe06101c0614c45565b613fed8e602084016133fa565b8152613ffc8e604084016133fa565b602082015261400e8e606084016133fa565b60408201526140208e608084016133fa565b606082015260a0820151608082015260c082015160a082015260e082015160c082015261010082015160e082015261012082015161010082015261014082015161012082015261016082015167ffffffffffffffff811115614080578a8bfd5b61408f8f6020838601016136ae565b6101408301525061018082015167ffffffffffffffff8111156140b0578a8bfd5b6140bf8f6020838601016136ae565b610160830152506101a082015167ffffffffffffffff8111156140e0578a8bfd5b6140ef8f6020838601016136ae565b610180830152506101c082015167ffffffffffffffff811115614110578a8bfd5b61411f8f6020838601016136ae565b6101a0830152508552506020938401939190910190600101613fb3565b505080965050505050604085015167ffffffffffffffff81111561415e578283fd5b61416a878288016135ea565b925050606085015167ffffffffffffffff811115614186578182fd5b613947878288016134d6565b6000606082840312156141a3578081fd5b6141ad6060614c45565b8251600781106141bb578283fd5b8152602083810151908201526040928301519281019290925250919050565b6000806000606084860312156141ee578081fd5b833567ffffffffffffffff811115614204578182fd5b61421086828701613703565b935050602084013561422181614cea565b929592945050506040919091013590565b60008060408385031215614244578182fd5b823567ffffffffffffffff8082111561425b578384fd5b61426786838701613703565b9350602085013591508082111561427c578283fd5b506138b285828601613660565b60008060006060848603121561429d578081fd5b833567ffffffffffffffff8111156142b3578182fd5b6142bf86828701613703565b9350506020840135915060408401356142d781614cea565b809150509250925092565b6000806000606084860312156142f6578081fd5b833567ffffffffffffffff8082111561430d578283fd5b81860160a0818903121561431f578384fd5b61432960a0614c45565b925080358352602081013560208401526040810135604084015261435088606083016133ef565b6060840152608081013582811115614366578485fd5b61437289828401613660565b60808501525091945050506020840135915061439185604086016133ef565b90509250925092565b6000602082840312156143ab578081fd5b5051919050565b6001600160a01b0316815260200190565b60006143cf83836144a2565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b8481101561442f578284038852614419848351614476565b6020988901989094509190910190600101614401565b50919695505050505050565b6000815180845260208401935060208301825b8281101561446c57815186526020958601959091019060010161444e565b5093949350505050565b6000815180845261448e816020860160208601614cb0565b601f01601f19169290920160200192915050565b8051600781106144ae57fe5b825260208181015190830152604090810151910152565b60006101c06144d58484516143d7565b60208301516144e760208601826143d7565b5060408301516144fa60408601826143d7565b50606083015161450d60608601826143d7565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e085015261010080840151818601525061012080840151818601525061014080840151828287015261456683870182614476565b915050610160915081840151858203838701526145838282614476565b92505050610180808401518583038287015261459f8382614476565b9150506101a0915081840151858203838701526145bc8282614476565b9695505050505050565b600082516145d8818460208701614cb0565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516146378185614937565b91508193508281028201838a01865b8381101561467057868303855261465e8383516144c5565b94860194925090850190600101614646565b505086810360408801528094508851925061468b8382614937565b94505050818701845b828110156146b5576146a78583516143b2565b945090830190600101614694565b5050505082810360608401526146cb818561443b565b979650505050505050565b60006001600160a01b0386168252608060208301526146f8608083018661443b565b828103604084015261470a818661443b565b838103606085015261471c8186614476565b98975050505050505050565b60006001600160a01b03851682526060602083015261474a6060830185614476565b9050826040830152949350505050565b60006001600160a01b0380871683526080602084015261477d60808401876144c5565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b818110156147e95783516147d581614ce0565b8352602093840193909201916001016147c2565b509095945050505050565b6000606082016060835280865161480b8184614937565b915060209250828801845b82811015614837576148298483516143c3565b935090840190600101614816565b5050508381038285015261484b818761443b565b84810360408601528551808252908301915082860190845b81811015614881578251151584529284019291840191600101614863565b509198975050505050505050565b600060208252611827602083018461443b565b6000604082526148b5604083018561443b565b602083820381850152818551808452828401915082838202850101838801865b8381101561490357601f198784030185526148f1838351614476565b948601949250908501906001016148d5565b50909998505050505050505050565b600060408252614925604083018561443b565b82810360208401526118db818561443b565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b038086166020840152808516604084015250608060608301526145bc6080830184614476565b60006001600160e01b0319871682526001600160a01b038616602083015260a060408301526149e660a083018661443b565b82810360608401526149f8818661443b565b8381036080850152614a0a8186614476565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614a496080830185614476565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614aa8606083018561443b565b82810360408401526145bc81856143e4565b6000602082526118276020830184614476565b600060808252614ae06080830187614476565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614b1957fe5b938152602081019290925260409091015290565b6060810160088510614b1957fe5b60208101614b4883614ce0565b91905290565b600060808252614b616080830187614476565b602083820381850152818751808452828401915082838202850101838a01865b83811015614baf57601f19878403018552614b9d8383516144c5565b94860194925090850190600101614b81565b50508681036040880152614bc3818a61443b565b94505050505082810360608401526146cb81856143e4565b60a08101614be982866144a2565b8360608301528215156080830152949350505050565b60006020825261182760208301846144c5565b600060408252614c2560408301856144c5565b82810360208401526118db8185614476565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614c6457600080fd5b604052919050565b600067ffffffffffffffff821115614c82578081fd5b5060209081020190565b600067ffffffffffffffff821115614ca2578081fd5b50601f01601f191660200190565b60005b83811015614ccb578181015183820152602001614cb3565b83811115614cda576000848401525b50505050565b6005811061073157fe5b6001600160a01b038116811461073157600080fd5b6001600160e01b03198116811461073157600080fdfea365627a7a72315820dc2fa6b39099cbef3d852e9f64db6bbaec7f3795508fc6ad2c43d1a719227f996c6578706572696d656e74616cf564736f6c63430005100040'; + '0x608060405234801561001057600080fd5b50600436106102775760003560e01c8063a5cd62ba11610160578063d186037f116100d8578063e4e6e7da1161008c578063ee18599711610071578063ee185997146105d8578063ef3bb097146105e0578063ff84e7cc146105e857610277565b8063e4e6e7da14610595578063e77286eb146105b657610277565b8063d3d862d1116100bd578063d3d862d114610540578063d469502814610553578063e25cabf71461057357610277565b8063d186037f1461051a578063d36379051461052d57610277565b8063bbb2dcf61161012f578063c82037ef11610114578063c82037ef146104dc578063ca49f47c146104e4578063d001c5dc1461050757610277565b8063bbb2dcf6146104b2578063c26cfecd146104d457610277565b8063a5cd62ba14610464578063a6627e9f14610484578063a7530f1214610497578063b43cffe11461049f57610277565b80637982653e116101f35780639baf2705116101c25780639eadc835116101a75780639eadc8351461041a578063a070cac81461043e578063a0901e511461045157610277565b80639baf2705146103fd5780639cd016051461041257610277565b80637982653e1461039657806379c9c426146103b65780637d727512146103c95780638f4ce479146103dc57610277565b80634dfdac201161024a57806363eb39921161022f57806363eb39921461033d5780636f83188e14610350578063750bdb301461037357610277565b80634dfdac20146102fd578063590aa8751461031d57610277565b806304a5618a1461027c5780630d7b7d76146102a75780632322cf76146102c857806346eb65cb146102e8575b600080fd5b61028f61028a366004614296565b6105f0565b60405161029e93929190614ce9565b60405180910390f35b6102ba6102b5366004613d5a565b610689565b60405161029e929190614ef1565b6102db6102d6366004613d5a565b6106ab565b60405161029e9190614bc6565b6102fb6102f6366004614296565b6106d3565b005b61031061030b366004613c75565b610757565b60405161029e9190614b1e565b61033061032b366004613c59565b6107da565b60405161029e9190614d49565b61033061034b366004613d9e565b610889565b61036361035e366004614296565b61093c565b60405161029e9493929190614ddd565b610386610381366004614296565b6109dc565b60405161029e9493929190614c07565b6103a96103a4366004614468565b610a6e565b60405161029e9190614dca565b6102db6103c4366004614570565b610b20565b6102db6103d7366004613d5a565b610ba1565b6103ef6103ea366004614296565b611497565b60405161029e929190614be4565b61040561152a565b60405161029e9190614871565b610405611539565b61042d610428366004614296565b611548565b60405161029e959493929190614c43565b6102db61044c366004614517565b6115ed565b61031061045f366004613e20565b61166e565b610477610472366004613eee565b6116e7565b60405161029e9190614a37565b610330610492366004613df5565b6117a1565b610405611851565b6103306104ad366004613cc3565b611860565b6104c56104c0366004614296565b611916565b60405161029e93929190614d14565b6102db6119a6565b6104056119ac565b6104f76104f2366004614296565b6119bb565b60405161029e9493929190614ca6565b610310610515366004613c75565b611a4d565b6102db610528366004613d5a565b611abb565b6103a961053b366004614468565b6123b7565b61033061054e366004613fc9565b612419565b610566610561366004614296565b61246e565b60405161029e9190614bcf565b610586610581366004613f72565b612511565b60405161029e93929190614a83565b6105a86105a3366004613c75565b612649565b60405161029e929190614ba1565b6105c96105c43660046144c0565b612662565b60405161029e93929190614e6a565b6104056129d2565b6104056129e1565b6104056129f0565b600080600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a856040518263ffffffff1660e01b815260040161062c9190614d49565b60606040518083038186803b15801561064457600080fd5b505af4158015610658573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061067c91908101906141ec565b9250925092509193909250565b6000806106968484610ba1565b91506106a28484611abb565b90509250929050565b60008060006106ba8585610689565b915091506106c882826129ff565b925050505b92915050565b6040517f46eb65cb00000000000000000000000000000000000000000000000000000000815273__$d8b635de2bf4a097b4e18b67d0fb68e779$__906346eb65cb90610723908490600401614d49565b60006040518083038186803b15801561073b57600080fd5b505af415801561074f573d6000803e3d6000fd5b505050505b50565b606060008251905080604051908082528060200260200182016040528015610789578160200160208202803883390190505b50915060005b8181146107d2576107b3858583815181106107a657fe5b6020026020010151611abb565b8382815181106107bf57fe5b602090810291909101015260010161078f565b505092915050565b6040517f590aa87500000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063590aa8759061082d908590600401614871565b60006040518083038186803b15801561084557600080fd5b505af4158015610859573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261088191908101906142c9565b90505b919050565b6040517f63eb399200000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__906363eb3992906108e0908790879087906004016149b7565b60006040518083038186803b1580156108f857600080fd5b505af415801561090c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261093491908101906142c9565b949350505050565b60608060608073__$fddee3b26a806429350031cf3481976d2e$__636f83188e866040518263ffffffff1660e01b81526004016109799190614d49565b60006040518083038186803b15801561099157600080fd5b505af41580156109a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614333565b93509350935093509193509193565b6000806000606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b8152600401610a1a9190614d49565b60006040518083038186803b158015610a3257600080fd5b505af4158015610a46573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614070565b600080546040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91639caa023b91610ad0916001600160a01b0316908890889088906004016149e9565b60206040518083038186803b158015610ae857600080fd5b505af4158015610afc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109349190810190614318565b600061093484610b9c6040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612a6c565b600080610bb4838263ffffffff612a8016565b90506001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415610c0f576000610bfb84601063ffffffff612ab916565b9050610c078186612aec565b925050611490565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415610e285760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401610c7b9190614d49565b60606040518083038186803b158015610c9357600080fd5b505af4158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccb91908101906141ec565b6040519194509250606091507f6352211e0000000000000000000000000000000000000000000000000000000090610d07908490602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051610d829190614855565b600060405180830381855afa9150503d8060008114610dbd576040519150601f19603f3d011682016040523d82523d6000602084013e610dc2565b606091505b50915091506000828015610dd7575081516020145b610de2576000610df3565b610df382600c63ffffffff612ab916565b9050896001600160a01b0316816001600160a01b031614610e15576000610e18565b60015b60ff169750505050505050611490565b6001600160e01b031981167fa7cb5fb70000000000000000000000000000000000000000000000000000000014156110c457600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b8152600401610e969190614d49565b60006040518083038186803b158015610eae57600080fd5b505af4158015610ec2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610eea91908101906140da565b5081519296509094509250905060005b8181146110ba57828181518110610f0d57fe5b602002602001015160001415610f22576110b2565b83516060907efdd58e00000000000000000000000000000000000000000000000000000000908b90879085908110610f5657fe5b6020026020010151604051602401610f6f929190614a1e565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060876001600160a01b031683604051610fea9190614855565b600060405180830381855afa9150503d8060008114611025576040519150601f19603f3d011682016040523d82523d6000602084013e61102a565b606091505b5091509150600082801561103f575081516020145b61104a57600061105b565b61105b82600063ffffffff612be416565b9050600087868151811061106b57fe5b6020026020010151828161107b57fe5b049050806110975760009b5050505050505050505050506106cd565b8b8110806110a357508b155b156110ac57809b505b50505050505b600101610efa565b5050505050611490565b6001600160e01b031981167fc339d10a0000000000000000000000000000000000000000000000000000000014156111f7576040516060907fa85e59e4000000000000000000000000000000000000000000000000000000009061113390869060009081908190602401614d5c565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925260045491519092506000916001600160a01b03169061119a908490614855565b600060405180830381855afa9150503d80600081146111d5576040519150601f19603f3d011682016040523d82523d6000602084013e6111da565b606091505b50509050806111ea5760006111ee565b6000195b93505050611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156113205760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016112639190614d49565b60006040518083038186803b15801561127b57600080fd5b505af415801561128f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112b79190810190614070565b5092509250506112c5612bf0565b6001600160a01b0316826001600160a01b03161480156112f257506005546001600160a01b038281169116145b1561131957600061130a611304612c08565b88612aec565b905061131581612c20565b9450505b5050611490565b6001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014156114905760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b815260040161138c9190614d49565b60006040518083038186803b1580156113a457600080fd5b505af41580156113b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113e0919081019061422c565b80519194509250905060005b81811461148b578381815181106113ff57fe5b60200260200101516000141561141457611483565b60006114338985848151811061142657fe5b6020026020010151610ba1565b9050600085838151811061144357fe5b6020026020010151828161145357fe5b0490508061146b5760009750505050505050506106cd565b87811080611477575087155b15611480578097505b50505b6001016113ec565b505050505b5092915050565b60008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__638f4ce479846040518263ffffffff1660e01b81526004016114d19190614d49565b604080518083038186803b1580156114e857600080fd5b505af41580156114fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115209190810190614039565b915091505b915091565b6004546001600160a01b031681565b6000546001600160a01b031681565b600080606080606073__$d8b635de2bf4a097b4e18b67d0fb68e779$__639eadc835876040518263ffffffff1660e01b81526004016115879190614d49565b60006040518083038186803b15801561159f57600080fd5b505af41580156115b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115db91908101906140da565b939a9299509097509550909350915050565b6000610934846116696040518060400160405280600b81526020017f30782050726f746f636f6c0000000000000000000000000000000000000000008152506040518060400160405280600581526020017f332e302e300000000000000000000000000000000000000000000000000000008152508787612a15565b612e1e565b606080825160405190808252806020026020018201604052801561169c578160200160208202803883390190505b50905060005b83518114611490578381815181106116b657fe5b60200260200101516001600160a01b0316318282815181106116d457fe5b60209081029190910101526001016116a2565b6000546040517f02cffc4500000000000000000000000000000000000000000000000000000000815260609173__$7a69f714cdde1cb6b62e3c39bc8c94deae$__916302cffc459161174d916001600160a01b039091169088908890889060040161489f565b60006040518083038186803b15801561176557600080fd5b505af4158015611779573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109349190810190613e53565b6040517fa6627e9f00000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063a6627e9f906117f69086908690600401614a1e565b60006040518083038186803b15801561180e57600080fd5b505af4158015611822573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261184a91908101906142c9565b9392505050565b6006546001600160a01b031681565b6040517fb43cffe100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063b43cffe1906118b9908890889088908890600401614965565b60006040518083038186803b1580156118d157600080fd5b505af41580156118e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261190d91908101906142c9565b95945050505050565b600060608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6856040518263ffffffff1660e01b81526004016119529190614d49565b60006040518083038186803b15801561196a57600080fd5b505af415801561197e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261067c919081019061422c565b60075481565b6005546001600160a01b031681565b6000806060600073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63ca49f47c866040518263ffffffff1660e01b81526004016119f99190614d49565b60006040518083038186803b158015611a1157600080fd5b505af4158015611a25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109cd9190810190614184565b606060008251905080604051908082528060200260200182016040528015611a7f578160200160208202803883390190505b50915060005b8181146107d257611a9c8585838151811061142657fe5b838281518110611aa857fe5b6020908102919091010152600101611a85565b600080611ace838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd7000000000000000000000000000000000000000000000000000000001415611c395760608073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63bbb2dcf6866040518263ffffffff1660e01b8152600401611b3c9190614d49565b60006040518083038186803b158015611b5457600080fd5b505af4158015611b68573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b90919081019061422c565b80519194509250905060005b818114611c2e57838181518110611baf57fe5b602002602001015160001415611bc457611c26565b6000611bd6898584815181106107a657fe5b90506000858381518110611be657fe5b60200260200101518281611bf657fe5b04905080611c0e5760009750505050505050506106cd565b87811080611c1a575087155b15611c23578097505b50505b600101611b9c565b506106cd9350505050565b6001600160e01b031981167ff47261b0000000000000000000000000000000000000000000000000000000001415611c9a576000611c7e84601063ffffffff612ab916565b600154909150610c0790829087906001600160a01b0316612e2d565b6001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415611fea5760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__6304a5618a866040518263ffffffff1660e01b8152600401611d069190614d49565b60606040518083038186803b158015611d1e57600080fd5b505af4158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d5691908101906141ec565b600254604051929550909350606092507fe985e9c50000000000000000000000000000000000000000000000000000000091611da2918a916001600160a01b0390911690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060846001600160a01b031683604051611e1d9190614855565b600060405180830381855afa9150503d8060008114611e58576040519150601f19603f3d011682016040523d82523d6000602084013e611e5d565b606091505b5091509150811580611e7157508051602014155b80611e8d5750611e8881600063ffffffff612be416565b600114155b15611fdb576040516060907f081812fc0000000000000000000000000000000000000000000000000000000090611ec8908790602401614bc6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050856001600160a01b031681604051611f3f9190614855565b600060405180830381855afa9150503d8060008114611f7a576040519150601f19603f3d011682016040523d82523d6000602084013e611f7f565b606091505b509093509150828015611f93575081516020145b8015611fc257506002546001600160a01b0316611fb783600c63ffffffff612ab916565b6001600160a01b0316145b611fcd576000611fd0565b60015b60ff169750506110ba565b60001996505050505050611490565b6001600160e01b031981167fa7cb5fb7000000000000000000000000000000000000000000000000000000001415612211576040517f9eadc83500000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__90639eadc8359061206f908790600401614d49565b60006040518083038186803b15801561208757600080fd5b505af415801561209b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120c391908101906140da565b5050600354604051929450606093507fe985e9c5000000000000000000000000000000000000000000000000000000009261210d925089916001600160a01b031690602401614885565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060006060836001600160a01b0316836040516121889190614855565b600060405180830381855afa9150503d80600081146121c3576040519150601f19603f3d011682016040523d82523d6000602084013e6121c8565b606091505b50915091508180156121db575080516020145b80156121f757506121f381600063ffffffff612be416565b6001145b612202576000612206565b6000195b955050505050611490565b6001600160e01b031981167fc339d10a00000000000000000000000000000000000000000000000000000000141561224d576000199150611490565b6001600160e01b031981167fdc1600f30000000000000000000000000000000000000000000000000000000014156114905760008073__$d8b635de2bf4a097b4e18b67d0fb68e779$__63750bdb30866040518263ffffffff1660e01b81526004016122b99190614d49565b60006040518083038186803b1580156122d157600080fd5b505af41580156122e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261230d9190810190614070565b50925092505061231b612bf0565b6001600160a01b0316826001600160a01b031614801561234857506005546001600160a01b038281169116145b1561239257600061236d61235a612c08565b60055489906001600160a01b0316612e2d565b905060001981146123865761238181612c20565b61238a565b6000195b9450506123ae565b6006546001600160a01b03828116911614156123ae5760001993505b50505092915050565b600080546040517f8dccde0400000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__91638dccde0491610ad0916001600160a01b0316908890889088906004016149e9565b6040517fd3d862d100000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d3d862d1906117f69086908690600401614b31565b6040517fd469502800000000000000000000000000000000000000000000000000000000815260009073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063d4695028906124c1908590600401614d49565b60206040518083038186803b1580156124d957600080fd5b505af41580156124ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610881919081019061401e565b60608060606000855190508060405190808252806020026020018201604052801561255657816020015b612543613691565b81526020019060019003908161253b5790505b50935080604051908082528060200260200182016040528015612583578160200160208202803883390190505b509250806040519080825280602002602001820160405280156125b0578160200160208202803883390190505b50915060005b818114612640576125ed8782815181106125cc57fe5b60200260200101518783815181106125e057fe5b6020026020010151612662565b87518890859081106125fb57fe5b6020026020010187858151811061260e57fe5b6020026020010187868151811061262157fe5b93151560209485029190910190930192909252919052526001016125b6565b50509250925092565b6060806126568484611a4d565b91506106a28484610757565b61266a613691565b600080546040517f9d3fa4b900000000000000000000000000000000000000000000000000000000815282916001600160a01b031690639d3fa4b9906126b4908890600401614e8e565b60606040518083038186803b1580156126cc57600080fd5b505afa1580156126e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127049190810190614420565b85516000546040517fa12dcc6f00000000000000000000000000000000000000000000000000000000815292955090916001600160a01b039091169063a12dcc6f906127569089908990600401614ecc565b60206040518083038186803b15801561276e57600080fd5b505afa158015612782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127a69190810190613ffe565b915060006127b387612f37565b905060006127d4886101800151896101400151612f5390919063ffffffff16565b1561280957612802826127f88a60c001518b60800151612f7890919063ffffffff16565b8a60a00151612f94565b9050612876565b60c0880151612825576128028289608001518a60a00151612f94565b6000612836848a61018001516106ab565b9050600061284d848b608001518c60a00151612f94565b90506000612864838c60c001518d60a00151612f94565b905061287082826129ff565b93505050505b61289a61289487604001518a60a00151612fb690919063ffffffff16565b826129ff565b9450600460005460208a01516040517f9caa023b00000000000000000000000000000000000000000000000000000000815273__$7a69f714cdde1cb6b62e3c39bc8c94deae$__92639caa023b92612905926001600160a01b03909216918e91908c906004016149e9565b60206040518083038186803b15801561291d57600080fd5b505af4158015612931573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129559190810190614318565b600481111561296057fe5b1461296c57600061296e565b845b945061297e886101600151612fd5565b61298757600094505b60e0880151158015906129a557506129a3886101a00151612fd5565b155b156129af57600094505b6003865160068111156129be57fe5b146129c857600094505b5050509250925092565b6001546001600160a01b031681565b6002546001600160a01b031681565b6003546001600160a01b031681565b6000818310612a0e578161184a565b5090919050565b8351602094850120835193850193909320604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815295860194909452928401929092526060830152608082015260a0902090565b600061184a82612a7b8561318a565b613205565b60008160040183511015612aa657612aa6612aa1600385518560040161323f565b6132ae565b5001602001516001600160e01b03191690565b60008160140183511015612ada57612ada612aa1600485518560140161323f565b5001601401516001600160a01b031690565b60405160009081906060906001600160a01b038616907f70a082310000000000000000000000000000000000000000000000000000000090612b32908790602401614871565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612b859190614855565b600060405180830381855afa9150503d8060008114612bc0576040519150601f19603f3d011682016040523d82523d6000602084013e612bc5565b606091505b5091509150818015612bd8575080516020145b156107d2576106c88160005b600061184a83836132b6565b736b175474e89094c44da98b954eedeac495271d0f90565b7306af07097c9eeb7fd685c692751d5c66db49c21590565b600080612c2b612c08565b6001600160a01b0316634ba2363a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612c6557600080fd5b505af1158015612c79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c9d91908101906142fc565b90506000816001600160a01b03166320aba08b6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612cdc57600080fd5b505af1158015612cf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d149190810190614629565b4211612d9257816001600160a01b031663c92aecc46040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612d5557600080fd5b505af1158015612d69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d8d9190810190614629565b612e05565b816001600160a01b0316639f678cca6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612dcd57600080fd5b505af1158015612de1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e059190810190614629565b9050610934816b033b2e3c9fd0803ce800000086612f94565b600061184a82612a7b856132e0565b60405160009081906060906001600160a01b038716907fdd62ed3e0000000000000000000000000000000000000000000000000000000090612e759088908890602401614885565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b0319909416939093179092529051612ec89190614855565b600060405180830381855afa9150503d8060008114612f03576040519150601f19603f3d011682016040523d82523d6000602084013e612f08565b606091505b5091509150818015612f1b575080516020145b15612f2e57612f2b816000612be4565b92505b50509392505050565b6000806000612f4584613399565b9150915061093482826129ff565b60008151835114801561184a5750508051602091820120825192909101919091201490565b60008282018381101561184a5761184a612aa16000868661359c565b600061093483612faa868563ffffffff6135bb16565b9063ffffffff6135ec16565b600082821115612fcf57612fcf612aa16002858561359c565b50900390565b60006020825181612fe257fe5b06600414612ff257506000610884565b6000613004838263ffffffff612a8016565b90506001600160e01b031981167f94cfcdd70000000000000000000000000000000000000000000000000000000014613041576001915050610884565b6040517fbbb2dcf600000000000000000000000000000000000000000000000000000000815260609073__$d8b635de2bf4a097b4e18b67d0fb68e779$__9063bbb2dcf690613094908790600401614d49565b60006040518083038186803b1580156130ac57600080fd5b505af41580156130c0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130e8919081019061422c565b80519093509150600090505b81811461317e576000613124600085848151811061310e57fe5b6020026020010151612a8090919063ffffffff16565b90506001600160e01b031981167f02571792000000000000000000000000000000000000000000000000000000001415613175576131628483613616565b1561317557600095505050505050610884565b506001016130f4565b50600195945050505050565b608081810151825160208085015160408087015160609788015186519685019690962082517fec69816980a3a3ca4554410e60253953e9ff375ba4536a98adfa15cc7154150881529485019590955290830191909152948101949094526001600160a01b039091169183019190915260a082015260c0902090565b6040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6060632800659560e01b84848460405160240161325e93929190614dbc565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b600081602001835110156132d7576132d7612aa1600585518560200161323f565b50016020015190565b6101408101516101608201516101808301516101a08401516000937ff80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a75349390929091602087101561332c57fe5b601f1987018051610140890180516101608b0180516101808d0180516101a08f0180519d89528c5160209d8e012087528b519b8d019b909b2084528951998c01999099208152875197909a019690962088526101e085209390945290529190529252919091529050919050565b60008060048361014001515110156133b657506000905080611525565b6101408301516000906133cf908263ffffffff612a8016565b90506001600160e01b031981167fdc1600f300000000000000000000000000000000000000000000000000000000141561356c576101408401516040517f750bdb3000000000000000000000000000000000000000000000000000000000815260009173__$d8b635de2bf4a097b4e18b67d0fb68e779$__9163750bdb309161345a91600401614d49565b60006040518083038186803b15801561347257600080fd5b505af4158015613486573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134ae9190810190614070565b506006549093506001600160a01b0380851691161415915061356a90505773__$ef9cb1cf4426222cc0af8204def2680bac$__63d12a7960866134ef613679565b6040518363ffffffff1660e01b815260040161350c929190614ea1565b60206040518083038186803b15801561352457600080fd5b505af4158015613538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061355c9190810190614629565b600019935093505050611525565b505b61357f8460000151856101400151610ba1565b6135928560000151866101400151611abb565b9250925050915091565b606063e946c1bb60e01b84848460405160240161325e93929190614d9a565b6000826135ca575060006106cd565b828202828482816135d757fe5b041461184a5761184a612aa16001868661359c565b60008161360257613602612aa16003858561359c565b600082848161360d57fe5b04949350505050565b8151600090600183015b818110156107d25761366185828151811061363757fe5b602002602001015186868151811061364b57fe5b6020026020010151612f5390919063ffffffff16565b15613671576001925050506106cd565b600101613620565b731e0447b19bb6ecfdae1e4ae1694b0c3659614e4e90565b6040805160608101909152806000815260006020820181905260409091015290565b80356106cd81614fa4565b80516106cd81614fa4565b600082601f8301126136d9578081fd5b81356136ec6136e782614f26565b614eff565b81815291506020808301908481018184028601820187101561370d57600080fd5b60005b8481101561148b57813561372381614fa4565b84529282019290820190600101613710565b600082601f830112613745578081fd5b81356137536136e782614f26565b8181529150602080830190840160005b838110156137905761377b8760208435890101613924565b83526020928301929190910190600101613763565b5050505092915050565b600082601f8301126137aa578081fd5b81516137b86136e782614f26565b8181529150602080830190840160005b83811015613790576137e08760208451890101613972565b835260209283019291909101906001016137c8565b600082601f830112613805578081fd5b81356138136136e782614f26565b8181529150602080830190840160005b838110156137905761383b87602084358901016139c7565b83526020928301929190910190600101613823565b600082601f830112613860578081fd5b813561386e6136e782614f26565b81815291506020808301908481018184028601820187101561388f57600080fd5b60005b8481101561148b57813584529282019290820190600101613892565b600082601f8301126138be578081fd5b81516138cc6136e782614f26565b8181529150602080830190848101818402860182018710156138ed57600080fd5b60005b8481101561148b578151845292820192908201906001016138f0565b80516001600160e01b0319811681146106cd57600080fd5b600082601f830112613934578081fd5b81356139426136e782614f46565b915080825283602082850101111561395957600080fd5b8060208401602084013760009082016020015292915050565b600082601f830112613982578081fd5b81516139906136e782614f46565b91508082528360208285010111156139a757600080fd5b611490816020840160208601614f6a565b8051600581106106cd57600080fd5b60006101c08083850312156139da578182fd5b6139e381614eff565b9150506139f083836136b3565b81526139ff83602084016136b3565b6020820152613a1183604084016136b3565b6040820152613a2383606084016136b3565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115613a8557600080fd5b613a9186838701613924565b83850152610160925082850135915080821115613aad57600080fd5b613ab986838701613924565b83850152610180925082850135915080821115613ad557600080fd5b613ae186838701613924565b838501526101a0925082850135915080821115613afd57600080fd5b50613b0a85828601613924565b82840152505092915050565b60006101c0808385031215613b29578182fd5b613b3281614eff565b915050613b3f83836136be565b8152613b4e83602084016136be565b6020820152613b6083604084016136be565b6040820152613b7283606084016136be565b60608201526080820151608082015260a082015160a082015260c082015160c082015260e082015160e08201526101008083015181830152506101208083015181830152506101408083015167ffffffffffffffff80821115613bd457600080fd5b613be086838701613972565b83850152610160925082850151915080821115613bfc57600080fd5b613c0886838701613972565b83850152610180925082850151915080821115613c2457600080fd5b613c3086838701613972565b838501526101a0925082850151915080821115613c4c57600080fd5b50613b0a85828601613972565b600060208284031215613c6a578081fd5b813561184a81614fa4565b60008060408385031215613c87578081fd5b8235613c9281614fa4565b9150602083013567ffffffffffffffff811115613cad578182fd5b613cb985828601613735565b9150509250929050565b60008060008060808587031215613cd8578182fd5b8435613ce381614fa4565b9350602085013567ffffffffffffffff80821115613cff578384fd5b613d0b88838901613850565b94506040870135915080821115613d20578384fd5b613d2c88838901613850565b93506060870135915080821115613d41578283fd5b50613d4e87828801613924565b91505092959194509250565b60008060408385031215613d6c578182fd5b8235613d7781614fa4565b9150602083013567ffffffffffffffff811115613d92578182fd5b613cb985828601613924565b600080600060608486031215613db2578081fd5b8335613dbd81614fa4565b9250602084013567ffffffffffffffff811115613dd8578182fd5b613de486828701613924565b925050604084013590509250925092565b60008060408385031215613e07578182fd5b8235613e1281614fa4565b946020939093013593505050565b600060208284031215613e31578081fd5b813567ffffffffffffffff811115613e47578182fd5b610934848285016136c9565b60006020808385031215613e65578182fd5b825167ffffffffffffffff811115613e7b578283fd5b80840185601f820112613e8c578384fd5b80519150613e9c6136e783614f26565b8281528381019082850185850284018601891015613eb8578687fd5b8693505b84841015613ee257613ece89826139b8565b835260019390930192918501918501613ebc565b50979650505050505050565b600080600060608486031215613f02578081fd5b833567ffffffffffffffff80821115613f19578283fd5b613f25878388016137f5565b94506020860135915080821115613f3a578283fd5b613f46878388016136c9565b93506040860135915080821115613f5b578283fd5b50613f6886828701613850565b9150509250925092565b60008060408385031215613f84578182fd5b823567ffffffffffffffff80821115613f9b578384fd5b613fa7868387016137f5565b93506020850135915080821115613fbc578283fd5b50613cb985828601613735565b60008060408385031215613fdb578182fd5b823567ffffffffffffffff80821115613ff2578384fd5b613fa786838701613850565b60006020828403121561400f578081fd5b8151801515811461184a578182fd5b60006020828403121561402f578081fd5b61184a838361390c565b6000806040838503121561404b578182fd5b614055848461390c565b9150602083015161406581614fa4565b809150509250929050565b60008060008060808587031215614085578182fd5b845161409081614fb9565b60208601519094506140a181614fa4565b60408601519093506140b281614fa4565b606086015190925067ffffffffffffffff8111156140ce578182fd5b613d4e87828801613972565b600080600080600060a086880312156140f1578283fd5b6140fb878761390c565b9450602086015161410b81614fa4565b604087015190945067ffffffffffffffff80821115614128578485fd5b61413489838a016138ae565b94506060880151915080821115614149578283fd5b61415589838a016138ae565b9350608088015191508082111561416a578283fd5b5061417788828901613972565b9150509295509295909350565b60008060008060808587031215614199578182fd5b6141a3868661390c565b935060208501516141b381614fa4565b604086015190935067ffffffffffffffff8111156141cf578283fd5b6141db87828801613972565b606096909601519497939650505050565b600080600060608486031215614200578081fd5b61420a858561390c565b9250602084015161421a81614fa4565b80925050604084015190509250925092565b600080600060608486031215614240578081fd5b835161424b81614fb9565b602085015190935067ffffffffffffffff80821115614268578283fd5b614274878388016138ae565b93506040860151915080821115614289578283fd5b50613f688682870161379a565b6000602082840312156142a7578081fd5b813567ffffffffffffffff8111156142bd578182fd5b61093484828501613924565b6000602082840312156142da578081fd5b815167ffffffffffffffff8111156142f0578182fd5b61093484828501613972565b60006020828403121561430d578081fd5b815161184a81614fa4565b600060208284031215614329578081fd5b61184a83836139b8565b60008060008060808587031215614348578182fd5b845167ffffffffffffffff8082111561435f578384fd5b61436b88838901613972565b9550602091508187015181811115614381578485fd5b80880189601f820112614392578586fd5b805191506143a26136e783614f26565b82815284810190828601885b858110156143d7576143c58e898451880101613b16565b845292870192908701906001016143ae565b505060408b01519098509450505050808211156143f2578384fd5b6143fe888389016138ae565b93506060870151915080821115614413578283fd5b50613d4e8782880161379a565b600060608284031215614431578081fd5b61443b6060614eff565b825160078110614449578283fd5b8152602083810151908201526040928301519281019290925250919050565b60008060006060848603121561447c578081fd5b833567ffffffffffffffff811115614492578182fd5b61449e868287016139c7565b93505060208401356144af81614fa4565b929592945050506040919091013590565b600080604083850312156144d2578182fd5b823567ffffffffffffffff808211156144e9578384fd5b6144f5868387016139c7565b9350602085013591508082111561450a578283fd5b50613cb985828601613924565b60008060006060848603121561452b578081fd5b833567ffffffffffffffff811115614541578182fd5b61454d868287016139c7565b93505060208401359150604084013561456581614fa4565b809150509250925092565b600080600060608486031215614584578081fd5b833567ffffffffffffffff8082111561459b578283fd5b81860160a081890312156145ad578384fd5b6145b760a0614eff565b925080358352602081013560208401526040810135604084015260608101356145df81614fa4565b60608401526080810135828111156145f5578485fd5b61460189828401613924565b60808501525091945050506020840135915061462085604086016136b3565b90509250925092565b60006020828403121561463a578081fd5b5051919050565b6001600160a01b0316815260200190565b600061465e8383614731565b505060600190565b6001600160a01b03169052565b600081518084526020840180819550602083028101915060208501845b848110156146be5782840388526146a8848351614705565b6020988901989094509190910190600101614690565b50919695505050505050565b6000815180845260208401935060208301825b828110156146fb5781518652602095860195909101906001016146dd565b5093949350505050565b6000815180845261471d816020860160208601614f6a565b601f01601f19169290920160200192915050565b80516007811061473d57fe5b825260208181015190830152604090810151910152565b60006101c0614764848451614666565b60208301516147766020860182614666565b5060408301516147896040860182614666565b50606083015161479c6060860182614666565b506080830151608085015260a083015160a085015260c083015160c085015260e083015160e08501526101008084015181860152506101208084015181860152506101408084015182828701526147f583870182614705565b915050610160915081840151858203838701526148128282614705565b92505050610180808401518583038287015261482e8382614705565b9150506101a09150818401518582038387015261484b8282614705565b9695505050505050565b60008251614867818460208701614f6a565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6000608082016001600160a01b038716835260206080818501528187516148c68185614bc6565b91508193508281028201838a01865b838110156148ff5786830385526148ed838351614754565b948601949250908501906001016148d5565b505086810360408801528094508851925061491a8382614bc6565b94505050818701845b8281101561494457614936858351614641565b945090830190600101614923565b50505050828103606084015261495a81856146ca565b979650505050505050565b60006001600160a01b03861682526080602083015261498760808301866146ca565b828103604084015261499981866146ca565b83810360608501526149ab8186614705565b98975050505050505050565b60006001600160a01b0385168252606060208301526149d96060830185614705565b9050826040830152949350505050565b60006001600160a01b03808716835260806020840152614a0c6080840187614754565b94166040830152506060015292915050565b6001600160a01b03929092168252602082015260400190565b602080825282518282018190526000918401906040840190835b81811015614a78578351614a6481614f9a565b835260209384019390920191600101614a51565b509095945050505050565b60006060820160608352808651614a9a8184614bc6565b915060209250828801845b82811015614ac657614ab8848351614652565b935090840190600101614aa5565b50505083810382850152614ada81876146ca565b84810360408601528551808252908301915082860190845b81811015614b10578251151584529284019291840191600101614af2565b509198975050505050505050565b60006020825261184a60208301846146ca565b600060408252614b4460408301856146ca565b602083820381850152818551808452828401915082838202850101838801865b83811015614b9257601f19878403018552614b80838351614705565b94860194925090850190600101614b64565b50909998505050505050505050565b600060408252614bb460408301856146ca565b828103602084015261190d81856146ca565b90815260200190565b6001600160e01b031991909116815260200190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b60006001600160e01b0319861682526001600160a01b0380861660208401528085166040840152506080606083015261484b6080830184614705565b60006001600160e01b0319871682526001600160a01b038616602083015260a06040830152614c7560a08301866146ca565b8281036060840152614c8781866146ca565b8381036080850152614c998186614705565b9998505050505050505050565b60006001600160e01b0319861682526001600160a01b038516602083015260806040830152614cd86080830185614705565b905082606083015295945050505050565b6001600160e01b03199390931683526001600160a01b03919091166020830152604082015260600190565b60006001600160e01b03198516825260606020830152614d3760608301856146ca565b828103604084015261484b8185614673565b60006020825261184a6020830184614705565b600060808252614d6f6080830187614705565b6001600160a01b03958616602084015293909416604082015260ff9190911660609091015292915050565b6060810160048510614da857fe5b938152602081019290925260409091015290565b6060810160088510614da857fe5b60208101614dd783614f9a565b91905290565b600060808252614df06080830187614705565b602083820381850152818751808452828401915082838202850101838a01865b83811015614e3e57601f19878403018552614e2c838351614754565b94860194925090850190600101614e10565b50508681036040880152614e52818a6146ca565b945050505050828103606084015261495a8185614673565b60a08101614e788286614731565b8360608301528215156080830152949350505050565b60006020825261184a6020830184614754565b600060408252614eb46040830185614754565b90506001600160a01b03831660208301529392505050565b600060408252614edf6040830185614754565b828103602084015261190d8185614705565b918252602082015260400190565b60405181810167ffffffffffffffff81118282101715614f1e57600080fd5b604052919050565b600067ffffffffffffffff821115614f3c578081fd5b5060209081020190565b600067ffffffffffffffff821115614f5c578081fd5b50601f01601f191660200190565b60005b83811015614f85578181015183820152602001614f6d565b83811115614f94576000848401525b50505050565b6005811061075457fe5b6001600160a01b038116811461075457600080fd5b6001600160e01b03198116811461075457600080fdfea365627a7a72315820f05bfff91cf6f387ce89e4ca5ccc415a4c291e39b349e6d1c630a5574b2fee306c6578706572696d656e74616cf564736f6c63430005100040'; public static contractName = 'DevUtils'; private readonly _methodABIIndex: { [name: string]: number } = {}; public static async deployFrom0xArtifactAsync( @@ -52,6 +52,7 @@ export class DevUtilsContract extends BaseContract { logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact }, exchange_: string, chaiBridge_: string, + dydxBridge_: string, ): Promise { assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ schemas.addressSchema, @@ -78,6 +79,7 @@ export class DevUtilsContract extends BaseContract { logDecodeDependenciesAbiOnly, exchange_, chaiBridge_, + dydxBridge_, ); } @@ -89,6 +91,7 @@ export class DevUtilsContract extends BaseContract { logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact }, exchange_: string, chaiBridge_: string, + dydxBridge_: string, ): Promise { assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ schemas.addressSchema, @@ -121,6 +124,7 @@ export class DevUtilsContract extends BaseContract { logDecodeDependenciesAbiOnly, exchange_, chaiBridge_, + dydxBridge_, ); } @@ -132,6 +136,7 @@ export class DevUtilsContract extends BaseContract { logDecodeDependencies: { [contractName: string]: ContractAbi }, exchange_: string, chaiBridge_: string, + dydxBridge_: string, ): Promise { assert.isHexString('bytecode', bytecode); assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ @@ -141,14 +146,14 @@ export class DevUtilsContract extends BaseContract { ]); const provider = providerUtils.standardizeOrThrow(supportedProvider); const constructorAbi = BaseContract._lookupConstructorAbi(abi); - [exchange_, chaiBridge_] = BaseContract._formatABIDataItemList( + [exchange_, chaiBridge_, dydxBridge_] = BaseContract._formatABIDataItemList( constructorAbi.inputs, - [exchange_, chaiBridge_], + [exchange_, chaiBridge_, dydxBridge_], BaseContract._bigNumberToString, ); const iface = new ethers.utils.Interface(abi); const deployInfo = iface.deployFunction; - const txData = deployInfo.encode(bytecode, [exchange_, chaiBridge_]); + const txData = deployInfo.encode(bytecode, [exchange_, chaiBridge_, dydxBridge_]); const web3Wrapper = new Web3Wrapper(provider); const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync( { @@ -167,7 +172,7 @@ export class DevUtilsContract extends BaseContract { txDefaults, logDecodeDependencies, ); - contractInstance.constructorArgs = [exchange_, chaiBridge_]; + contractInstance.constructorArgs = [exchange_, chaiBridge_, dydxBridge_]; return contractInstance; } @@ -186,6 +191,10 @@ export class DevUtilsContract extends BaseContract { name: 'chaiBridge_', type: 'address', }, + { + name: 'dydxBridge_', + type: 'address', + }, ], outputs: [], payable: false, @@ -502,6 +511,20 @@ export class DevUtilsContract extends BaseContract { stateMutability: 'pure', type: 'function', }, + { + constant: true, + inputs: [], + name: 'dydxBridgeAddress', + outputs: [ + { + name: '', + type: 'address', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, { constant: true, inputs: [ @@ -1955,6 +1978,26 @@ export class DevUtilsContract extends BaseContract { }, }; } + public dydxBridgeAddress(): ContractFunctionObj { + const self = (this as any) as DevUtilsContract; + const functionSignature = 'dydxBridgeAddress()'; + + return { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { ...callData, data: this.getABIEncodedTransactionData() }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, []); + }, + }; + } /** * Encode ERC-1155 asset data into the format described in the AssetProxy contract specification. * @param tokenAddress The address of the ERC-1155 contract hosting the From 58f772c74eadd2e38133ccb7530dd0c8bf392bd0 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 13:31:56 -0500 Subject: [PATCH 13/18] foo --- .../contracts/src/bridges/DydxBridge.sol | 2 +- .../contracts/src/interfaces/IDydxBridge.sol | 2 +- .../dev-utils/contracts/src/AssetBalance.sol | 8 +- contracts/dev-utils/contracts/src/D18.sol | 13 +- .../contracts/src/LibDydxBalance.sol | 39 +++-- .../contracts/src/OrderValidationUtils.sol | 139 +++++++++--------- .../dev-utils/dydx_order_validation_test.ts | 2 +- 7 files changed, 108 insertions(+), 97 deletions(-) diff --git a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol index 8176c2507c..60bcd57ead 100644 --- a/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol +++ b/contracts/asset-proxy/contracts/src/bridges/DydxBridge.sol @@ -229,7 +229,7 @@ contract DydxBridge is withdrawAction = IDydx.ActionArgs({ actionType: IDydx.ActionType.Withdraw, // withdraw tokens. amount: amountToWithdraw, // amount to withdraw. - accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`. + accountIdx: bridgeAction.accountIdx, // index in the `accounts` when calling `operate`. primaryMarketId: bridgeAction.marketId, // indicates which token to withdraw. otherAddress: withdrawTo, // withdraw tokens to this address. // unused parameters diff --git a/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol b/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol index 779bf72fdc..057f8ec4c0 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IDydxBridge.sol @@ -29,7 +29,7 @@ interface IDydxBridge { struct BridgeAction { BridgeActionType actionType; // Action to run on dydx account. - uint256 accountIdx; // Index in `BridgeData.accountNumbers` for this action. + uint256 accountIdx; // Index in `BridgeData.accountNumbers` for this action. uint256 marketId; // Market to operate on. uint256 conversionRateNumerator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). uint256 conversionRateDenominator; // Optional. If set, transfer amount is scaled by (conversionRateNumerator/conversionRateDenominator). diff --git a/contracts/dev-utils/contracts/src/AssetBalance.sol b/contracts/dev-utils/contracts/src/AssetBalance.sol index ca4dd57a6f..a71d116ffb 100644 --- a/contracts/dev-utils/contracts/src/AssetBalance.sol +++ b/contracts/dev-utils/contracts/src/AssetBalance.sol @@ -269,14 +269,14 @@ contract AssetBalance is } else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) { // Get address of ERC20 token and bridge contract - (, address tokenAddress, address bridgeAddress,) = LibAssetData.decodeERC20BridgeAssetData(assetData); + (, address tokenAddress, address bridgeAddress,) = + LibAssetData.decodeERC20BridgeAssetData(assetData); if (tokenAddress == _getDaiAddress() && bridgeAddress == chaiBridgeAddress) { uint256 chaiAllowance = LibERC20Token.allowance(_getChaiAddress(), ownerAddress, chaiBridgeAddress); // Dai allowance is unlimited if Chai allowance is unlimited allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance); } else if (bridgeAddress == dydxBridgeAddress) { - // Dydx bridges always have infinite allowance. - allowance = _MAX_UINT256; + allowance = LibDydxBalance.getDydxMakerAllowance(ownerAddress, bridgeAddress, _getDydxAddress()); } // Allowance will be 0 if bridge is not supported } @@ -376,7 +376,7 @@ contract AssetBalance is if (bridgeAddress == dydxBridgeAddress) { return ( LibDydxBalance.getDydxMakerBalance(order, _getDydxAddress()), - _MAX_UINT256 + getAssetProxyAllowance(order.makerAddress, order.makerAssetData) ); } } diff --git a/contracts/dev-utils/contracts/src/D18.sol b/contracts/dev-utils/contracts/src/D18.sol index 9334f3057e..d6fde12b0f 100644 --- a/contracts/dev-utils/contracts/src/D18.sol +++ b/contracts/dev-utils/contracts/src/D18.sol @@ -154,7 +154,6 @@ library D18 { pure returns (int256 r) { - require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); r = _div(_mul(a, DECIMAL_ONE), b); } @@ -184,8 +183,8 @@ library D18 { pure returns (int256 r) { - require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - require(uint256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); r = _div(_mul(int256(a), DECIMAL_ONE), int256(b)); } @@ -195,7 +194,7 @@ library D18 { pure returns (int256 r) { - require(uint256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); r = int256(a); } @@ -228,8 +227,8 @@ library D18 { pure returns (int256 r) { - require(b != 0, "D18/DECIMAL_DIVISION_BY_ZERO"); - require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_MUL_OVERFLOW"); + require(b != 0, "D18/DECIMAL_DIV_BY_ZERO"); + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_DIV_OVERFLOW"); r = a / b; } @@ -242,7 +241,7 @@ library D18 { r = a + b; require( !((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)), - "D18/DECIMAL_MUL_OVERFLOW" + "D18/DECIMAL_ADD_OVERFLOW" ); } diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index 822bd20bf3..7327af4256 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -53,6 +53,22 @@ library LibDydxBalance { IDydxBridge.BridgeAction[] actions; } + /// @dev Gets the maker asset allowance for a Dydx bridge order. + /// @param makerAddress The maker of the order. + /// @param bridgeAddress The address of the Dydx bridge. + /// @param dydx The Dydx contract address. + /// @return allowance The maker asset allowance. + function getDydxMakerAllowance(address makerAddress, address bridgeAddress, address dydx) + public + view + returns (uint256 allowance) + { + // Allowance is infinite if the dydx bridge is an operator for the maker. + return IDydx(dydx).getIsLocalOperator(makerAddress, bridgeAddress) + ? uint256(-1) : 0; + } + + /// @dev Gets the maker allowance for a /// @dev Get the maker asset balance of an order with a `DydxBridge` maker asset. /// @param order An order with a dydx maker asset. /// @param dydx The address of the dydx contract. @@ -63,10 +79,6 @@ library LibDydxBalance { returns (uint256 balance) { BalanceCheckInfo memory info = _getBalanceCheckInfo(order, dydx); - // The Dydx bridge must be an operator for the maker. - if (!info.dydx.getIsLocalOperator(info.makerAddress, info.bridgeAddress)) { - return 0; - } // Actions must be well-formed. if (!_areActionsWellFormed(info)) { return 0; @@ -80,7 +92,8 @@ library LibDydxBalance { // The maker balance is the smaller of: return LibSafeMath.min256( // How many times we can execute all the deposit actions. - _getDepositableMakerAmount(info), + // _getDepositableMakerAmount(info), + uint256(-1), // How many times we can execute all the actions before the an // account becomes undercollateralized. _getSolventMakerAmount(info) @@ -246,6 +259,11 @@ library LibDydxBalance { if (D18.div(dd, db) >= minCr) { continue; } + // If the adjusted deposit rates are equal, the account will remain + // at the same level of collateralization. + if (D18.mul(minCr, db) == dd) { + continue; + } // The collateralization ratio for this account, parameterized by // `t` (maker amount), is given by: // `cr = (supplyValue + t * dd) / (borrowValue + t * db)` @@ -277,9 +295,11 @@ library LibDydxBalance { LibAssetData.decodeERC20BridgeAssetData(order.makerAssetData); info.dydx = IDydx(dydx); info.makerAddress = order.makerAddress; - if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) { - (, info.takerTokenAddress) = - LibAssetData.decodeERC20AssetData(order.takerAssetData); + if (order.takerAssetData.length == 36) { + if (order.takerAssetData.readBytes4(0) == IAssetData(0).ERC20Token.selector) { + (, info.takerTokenAddress) = + LibAssetData.decodeERC20AssetData(order.takerAssetData); + } } info.orderMakerToTakerRate = D18.div(order.takerAssetAmount, order.makerAssetAmount); (IDydxBridge.BridgeData memory bridgeData) = @@ -351,8 +371,7 @@ library LibDydxBalance { } } - /// @dev Returns the conversion rate for an action, expressed as units - /// of the market token. + /// @dev Convert a `D18` fraction of 1 token to the equivalent integer wei. /// @param token Address the of the token. /// @param units Token units expressed with 18 digit precision. function _toWei(address token, uint256 units) diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index 2b34666ca3..72d71f7920 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -72,65 +72,73 @@ contract OrderValidationUtils is // Get the amount of `takerAsset` that is transferable to maker given the // transferability of `makerAsset`, `makerFeeAsset`, // and the total amounts specified in the order - uint256 transferableTakerAssetAmount; - if (order.makerAssetData.equals(order.makerFeeAssetData)) { - // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is - // transferableMakerAssetAmount / (makerAssetAmount + makerFee) - transferableTakerAssetAmount = LibMath.getPartialAmountFloor( - transferableMakerAssetAmount, - order.makerAssetAmount.safeAdd(order.makerFee), - order.takerAssetAmount - ); - } else { - // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) - if (order.makerFee == 0) { - transferableTakerAssetAmount = LibMath.getPartialAmountFloor( - transferableMakerAssetAmount, - order.makerAssetAmount, - order.takerAssetAmount - ); + uint256 transferableTakerAssetAmount = transferableMakerAssetAmount; + // if (order.makerAssetData.equals(order.makerFeeAssetData)) { + // // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is + // // transferableMakerAssetAmount / (makerAssetAmount + makerFee) + // transferableTakerAssetAmount = LibMath.getPartialAmountFloor( + // transferableMakerAssetAmount, + // order.makerAssetAmount.safeAdd(order.makerFee), + // order.takerAssetAmount + // ); + // } else { + // // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) + // if (order.makerFee == 0) { + // transferableTakerAssetAmount = LibMath.getPartialAmountFloor( + // transferableMakerAssetAmount, + // order.makerAssetAmount, + // order.takerAssetAmount + // ); + // + // // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of + // // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) + // } else { + // // Get the transferable amount of the `makerFeeAsset` + // uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount( + // makerAddress, + // order.makerFeeAssetData + // ); + // uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor( + // transferableMakerAssetAmount, + // order.makerAssetAmount, + // order.takerAssetAmount + // ); + // uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor( + // transferableMakerFeeAssetAmount, + // order.makerFee, + // order.takerAssetAmount + // ); + // transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); + // } + // } + // + // // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` + // fillableTakerAssetAmount = LibSafeMath.min256( + // order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount), + // transferableTakerAssetAmount + // ); + // + // // Execute the maker transfers. + // fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults( + // exchangeAddress, + // order, + // order.takerAddress, + // fillableTakerAssetAmount + // ) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0; + // + // if (!_isAssetDataValid(order.takerAssetData)) { + // fillableTakerAssetAmount = 0; + // } + // + // if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) { + // fillableTakerAssetAmount = 0; + // } + // + // if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { + // fillableTakerAssetAmount = 0; + // } - // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of - // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) - } else { - // Get the transferable amount of the `makerFeeAsset` - uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount( - makerAddress, - order.makerFeeAssetData - ); - uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor( - transferableMakerAssetAmount, - order.makerAssetAmount, - order.takerAssetAmount - ); - uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor( - transferableMakerFeeAssetAmount, - order.makerFee, - order.takerAssetAmount - ); - transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); - } - } - - // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` - fillableTakerAssetAmount = LibSafeMath.min256( - order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount), - transferableTakerAssetAmount - ); - - // Ensure that all of the asset data is valid. Fee asset data only needs - // to be valid if the fees are nonzero. - if (!_areOrderAssetDatasValid(order)) { - fillableTakerAssetAmount = 0; - } - - // If the order is not fillable, then the fillable taker asset amount is - // zero by definition. - if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { - fillableTakerAssetAmount = 0; - } - - return (orderInfo, fillableTakerAssetAmount, isValidSignature); + return (orderInfo, transferableMakerAssetAmount, isValidSignature); } /// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable. @@ -202,21 +210,6 @@ contract OrderValidationUtils is return transferableAssetAmount; } - /// @dev Checks that the asset data contained in a ZeroEx is valid and returns - /// a boolean that indicates whether or not the asset data was found to be valid. - /// @param order A ZeroEx order to validate. - /// @return The validatity of the asset data. - function _areOrderAssetDatasValid(LibOrder.Order memory order) - internal - pure - returns (bool) - { - return _isAssetDataValid(order.makerAssetData) && - (order.makerFee == 0 || _isAssetDataValid(order.makerFeeAssetData)) && - _isAssetDataValid(order.takerAssetData) && - (order.takerFee == 0 || _isAssetDataValid(order.takerFeeAssetData)); - } - /// @dev This function handles the edge cases around taker validation. This function /// currently attempts to find duplicate ERC721 token's in the taker /// multiAssetData. diff --git a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts index 9986bd759b..855474eb6e 100644 --- a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts +++ b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts @@ -255,7 +255,7 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); }); - it('validates a perpetually solvent order', async () => { + it.only('validates a perpetually solvent order', async () => { // This account is not very well collateralized, but the deposit rate // will keep the collateralization ratio the same or better. const accountId = randomAccountId(); From f9a7857a907fbb2ed7fc3e03c272345f59044644 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 15:43:27 -0500 Subject: [PATCH 14/18] `@0x/contracts-dev-utils`: Address review comments. --- .../contracts/src/LibDydxBalance.sol | 3 +- .../contracts/src/OrderValidationUtils.sol | 134 +++++++++--------- 2 files changed, 68 insertions(+), 69 deletions(-) diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index 7327af4256..8718f5368c 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -92,8 +92,7 @@ library LibDydxBalance { // The maker balance is the smaller of: return LibSafeMath.min256( // How many times we can execute all the deposit actions. - // _getDepositableMakerAmount(info), - uint256(-1), + _getDepositableMakerAmount(info), // How many times we can execute all the actions before the an // account becomes undercollateralized. _getSolventMakerAmount(info) diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index 72d71f7920..ea319c07ee 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -72,73 +72,73 @@ contract OrderValidationUtils is // Get the amount of `takerAsset` that is transferable to maker given the // transferability of `makerAsset`, `makerFeeAsset`, // and the total amounts specified in the order - uint256 transferableTakerAssetAmount = transferableMakerAssetAmount; - // if (order.makerAssetData.equals(order.makerFeeAssetData)) { - // // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is - // // transferableMakerAssetAmount / (makerAssetAmount + makerFee) - // transferableTakerAssetAmount = LibMath.getPartialAmountFloor( - // transferableMakerAssetAmount, - // order.makerAssetAmount.safeAdd(order.makerFee), - // order.takerAssetAmount - // ); - // } else { - // // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) - // if (order.makerFee == 0) { - // transferableTakerAssetAmount = LibMath.getPartialAmountFloor( - // transferableMakerAssetAmount, - // order.makerAssetAmount, - // order.takerAssetAmount - // ); - // - // // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of - // // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) - // } else { - // // Get the transferable amount of the `makerFeeAsset` - // uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount( - // makerAddress, - // order.makerFeeAssetData - // ); - // uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor( - // transferableMakerAssetAmount, - // order.makerAssetAmount, - // order.takerAssetAmount - // ); - // uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor( - // transferableMakerFeeAssetAmount, - // order.makerFee, - // order.takerAssetAmount - // ); - // transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); - // } - // } - // - // // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` - // fillableTakerAssetAmount = LibSafeMath.min256( - // order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount), - // transferableTakerAssetAmount - // ); - // - // // Execute the maker transfers. - // fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults( - // exchangeAddress, - // order, - // order.takerAddress, - // fillableTakerAssetAmount - // ) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0; - // - // if (!_isAssetDataValid(order.takerAssetData)) { - // fillableTakerAssetAmount = 0; - // } - // - // if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) { - // fillableTakerAssetAmount = 0; - // } - // - // if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { - // fillableTakerAssetAmount = 0; - // } + uint256 transferableTakerAssetAmount; + if (order.makerAssetData.equals(order.makerFeeAssetData)) { + // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is + // transferableMakerAssetAmount / (makerAssetAmount + makerFee) + transferableTakerAssetAmount = LibMath.getPartialAmountFloor( + transferableMakerAssetAmount, + order.makerAssetAmount.safeAdd(order.makerFee), + order.takerAssetAmount + ); + } else { + // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) + if (order.makerFee == 0) { + transferableTakerAssetAmount = LibMath.getPartialAmountFloor( + transferableMakerAssetAmount, + order.makerAssetAmount, + order.takerAssetAmount + ); - return (orderInfo, transferableMakerAssetAmount, isValidSignature); + // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of + // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) + } else { + // Get the transferable amount of the `makerFeeAsset` + uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount( + makerAddress, + order.makerFeeAssetData + ); + uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor( + transferableMakerAssetAmount, + order.makerAssetAmount, + order.takerAssetAmount + ); + uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor( + transferableMakerFeeAssetAmount, + order.makerFee, + order.takerAssetAmount + ); + transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); + } + } + + // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` + fillableTakerAssetAmount = LibSafeMath.min256( + order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount), + transferableTakerAssetAmount + ); + + // Execute the maker transfers. + fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults( + exchangeAddress, + order, + order.takerAddress, + fillableTakerAssetAmount + ) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0; + + if (!_isAssetDataValid(order.takerAssetData)) { + fillableTakerAssetAmount = 0; + } + + if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) { + fillableTakerAssetAmount = 0; + } + + if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { + fillableTakerAssetAmount = 0; + } + + return (orderInfo, fillableTakerAssetAmount, isValidSignature); } /// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable. @@ -207,7 +207,7 @@ contract OrderValidationUtils is { (uint256 balance, uint256 allowance) = _getConvertibleMakerBalanceAndAssetProxyAllowance(order); transferableAssetAmount = LibSafeMath.min256(balance, allowance); - return transferableAssetAmount; + return LibSafeMath.min256(transferableAssetAmount, order.makerAssetAmount); } /// @dev This function handles the edge cases around taker validation. This function From a04722b61234395c89e10a00e0221b58257216c3 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 15:44:04 -0500 Subject: [PATCH 15/18] `@0x/contracts-integrations`: Add USDC->DAI forked dydx bridge order validation. --- .../dev-utils/dydx_order_validation_test.ts | 303 +++++++++++++----- 1 file changed, 217 insertions(+), 86 deletions(-) diff --git a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts index 855474eb6e..102a7869ad 100644 --- a/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts +++ b/contracts/integrations/test/dev-utils/dydx_order_validation_test.ts @@ -31,11 +31,12 @@ enum DydxAssetReference { Target = 1, } -const MAKER_ADDRESS = '0x3a9F7C8cA36C42d7035E87C3304eE5cBd353a532'; +const CHONKY_DAI_WALLET = '0x3a9F7C8cA36C42d7035E87C3304eE5cBd353a532'; +const CHONKY_USDC_WALLET = '0x1EDA7056fF11C9817038E0020C3a6F1d6A8Ec32e'; blockchainTests.configure({ fork: { - unlockedAccounts: [MAKER_ADDRESS], + unlockedAccounts: [CHONKY_DAI_WALLET, CHONKY_USDC_WALLET], }, }); @@ -57,11 +58,10 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { }; const DAI_DECIMALS = TOKEN_INFO[DAI_ADDRESS].decimals; const USDC_DECIMALS = TOKEN_INFO[USDC_ADDRESS].decimals; - const DAI_MARKET_ID = TOKEN_INFO[DAI_ADDRESS].marketId; - const USDC_MARKET_ID = TOKEN_INFO[USDC_ADDRESS].marketId; let bridge: DydxBridgeContract; let dydx: IDydxContract; let dai: ERC20TokenContract; + let usdc: ERC20TokenContract; let devUtils: DevUtilsContract; let accountOwner: string; let minMarginRatio: number; @@ -70,6 +70,7 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { [accountOwner] = await env.getAccountAddressesAsync(); dydx = new IDydxContract(DYDX_ADDRESS, env.provider, env.txDefaults); dai = new ERC20TokenContract(DAI_ADDRESS, env.provider, env.txDefaults); + usdc = new ERC20TokenContract(USDC_ADDRESS, env.provider, env.txDefaults); bridge = await DydxBridgeContract.deployFrom0xArtifactAsync( assetProxyArtifacts.DydxBridge, env.provider, @@ -89,21 +90,36 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { minMarginRatio = toTokenUnitAmount((await dydx.getRiskParams().callAsync()).marginRatio.value) .plus(1) .toNumber(); - // Deposit Dai collateral. - await dai.approve(DYDX_ADDRESS, constants.MAX_UINT256).awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + // Set approvals and operators. + await dai + .approve(DYDX_ADDRESS, constants.MAX_UINT256) + .awaitTransactionSuccessAsync({ from: CHONKY_DAI_WALLET }); + await usdc + .approve(DYDX_ADDRESS, constants.MAX_UINT256) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET }); await dydx .setOperators([{ operator: bridge.address, trusted: true }]) - .awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + .awaitTransactionSuccessAsync({ from: CHONKY_DAI_WALLET }); + await dydx + .setOperators([{ operator: bridge.address, trusted: true }]) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET }); }); async function depositAndWithdrawAsync( + makerAddress: string, accountId: BigNumber, depositSize: Numberish = 0, withdrawSize: Numberish = 0, ): Promise { + const fromToken = makerAddress === CHONKY_DAI_WALLET ? DAI_ADDRESS : USDC_ADDRESS; + const toToken = fromToken === DAI_ADDRESS ? USDC_ADDRESS : DAI_ADDRESS; + const fromDecimals = TOKEN_INFO[fromToken].decimals; + const fromMarketId = TOKEN_INFO[fromToken].marketId; + const toDecimals = TOKEN_INFO[toToken].decimals; + const toMarketId = TOKEN_INFO[toToken].marketId; await dydx .operate( - [{ owner: MAKER_ADDRESS, number: accountId }], + [{ owner: makerAddress, number: accountId }], [ ...(depositSize > 0 ? [ @@ -114,11 +130,11 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { sign: true, denomination: DydxAssetDenomination.Wei, ref: DydxAssetReference.Delta, - value: fromTokenUnitAmount(depositSize, DAI_DECIMALS), + value: fromTokenUnitAmount(depositSize, fromDecimals), }, - primaryMarketId: new BigNumber(DAI_MARKET_ID), + primaryMarketId: new BigNumber(fromMarketId), secondaryMarketId: new BigNumber(constants.NULL_ADDRESS), - otherAddress: MAKER_ADDRESS, + otherAddress: makerAddress, otherAccountIdx: ZERO, data: constants.NULL_BYTES, }, @@ -133,11 +149,11 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { sign: false, denomination: DydxAssetDenomination.Wei, ref: DydxAssetReference.Delta, - value: fromTokenUnitAmount(withdrawSize, USDC_DECIMALS), + value: fromTokenUnitAmount(withdrawSize, toDecimals), }, - primaryMarketId: new BigNumber(USDC_MARKET_ID), + primaryMarketId: new BigNumber(toMarketId), secondaryMarketId: new BigNumber(constants.NULL_ADDRESS), - otherAddress: MAKER_ADDRESS, + otherAddress: makerAddress, otherAccountIdx: ZERO, data: constants.NULL_BYTES, }, @@ -145,7 +161,7 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { : []), ], ) - .awaitTransactionSuccessAsync({ from: MAKER_ADDRESS }); + .awaitTransactionSuccessAsync({ from: makerAddress }); } const SECONDS_IN_ONE_YEAR = 365 * 24 * 60 * 60; @@ -155,7 +171,7 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { chainId: 1, exchangeAddress: contractAddresses.exchange, expirationTimeSeconds: new BigNumber(Math.floor(Date.now() / 1000 + SECONDS_IN_ONE_YEAR)), - makerAddress: MAKER_ADDRESS, + makerAddress: CHONKY_DAI_WALLET, takerAddress: constants.NULL_ADDRESS, senderAddress: constants.NULL_ADDRESS, feeRecipientAddress: constants.NULL_ADDRESS, @@ -240,84 +256,199 @@ blockchainTests.fork('DevUtils dydx order validation tests', env => { return new BigNumber(hexUtils.random()); } - it('validates a fully solvent order', async () => { - // This account is collateralized enough to fill the order with just - // withdraws. - const accountId = randomAccountId(); - await depositAndWithdrawAsync(accountId, 200, 0); - const order = createOrder({ - makerAssetData: encodeDydxBridgeAssetData({ - accountId, - depositRate: 0, - }), + describe('DAI -> USDC', () => { + const makerAddress = CHONKY_DAI_WALLET; + function _createOrder(fields: Partial = {}): Order { + return createOrder(fields); + } + + it('validates a fully solvent order', async () => { + // This account is collateralized enough to fill the order with just + // withdraws. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 200, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); + }); + + it('validates a perpetually solvent order', async () => { + // This account is not very well collateralized, but the deposit rate + // will keep the collateralization ratio the same or better. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); + }); + + it('validates a partially solvent order with an inadequate deposit', async () => { + // This account is not very well collateralized and the deposit rate is + // also too low to sustain the collateralization ratio for the full order. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio * 0.95, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + it('validates a partially solvent order with no deposit', async () => { + // This account is not very well collateralized and there is no deposit + // to keep the collateralization ratio up. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + // TODO(dorothy-zbornak): We can't actually create an account that's below + // the margin ratio without replacing the price oracles. + it('invalidates a virtually insolvent order', async () => { + // This account has a collateralization ratio JUST above the + // minimum margin ratio, so it can only withdraw nearly zero maker tokens. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 1 / (minMarginRatio + 3e-4)); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + // Price fluctuations will cause this to be a little above zero, so we + // don't compare to zero. + expect(fillableTakerAssetAmount).to.bignumber.lt(fromTokenUnitAmount(1e-3, DAI_DECIMALS)); }); - const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); - expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); }); - it.only('validates a perpetually solvent order', async () => { - // This account is not very well collateralized, but the deposit rate - // will keep the collateralization ratio the same or better. - const accountId = randomAccountId(); - await depositAndWithdrawAsync(accountId, 1, 0); - const order = createOrder({ - makerAssetData: encodeDydxBridgeAssetData({ - accountId, - depositRate: minMarginRatio, - }), - }); - const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); - expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); - }); + describe('USDC -> DAI', () => { + const makerAddress = CHONKY_USDC_WALLET; + function _createOrder(fields: Partial = {}): Order { + return createOrder({ + makerAddress, + takerAssetData: encodeERC20AssetData(USDC_ADDRESS), + makerAssetData: encodeDydxBridgeAssetData({ + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + makerAssetAmount: fromTokenUnitAmount(100, DAI_DECIMALS), + takerAssetAmount: fromTokenUnitAmount(100, USDC_DECIMALS), + ...fields, + }); + } - it('validates a partially solvent order with an inadequate deposit', async () => { - // This account is not very well collateralized and the deposit rate is - // also too low to sustain the collateralization ratio for the full order. - const accountId = randomAccountId(); - await depositAndWithdrawAsync(accountId, 1, 0); - const order = createOrder({ - makerAssetData: encodeDydxBridgeAssetData({ - accountId, - depositRate: minMarginRatio * 0.95, - }), + it('validates a fully solvent order', async () => { + // This account is collateralized enough to fill the order with just + // withdraws. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 200, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); }); - const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); - expect(fillableTakerAssetAmount).to.bignumber.gt(0); - expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); - }); - it('validates a partially solvent order with no deposit', async () => { - // This account is not very well collateralized and there is no deposit - // to keep the collateralization ratio up. - const accountId = randomAccountId(); - await depositAndWithdrawAsync(accountId, 1, 0); - const order = createOrder({ - makerAssetData: encodeDydxBridgeAssetData({ - accountId, - depositRate: 0, - }), + it('validates a perpetually solvent order', async () => { + // This account is not very well collateralized, but the deposit rate + // will keep the collateralization ratio the same or better. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio, + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.eq(order.takerAssetAmount); }); - const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); - expect(fillableTakerAssetAmount).to.bignumber.gt(0); - expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); - }); - // TODO(dorothy-zbornak): We can't actually create an account that's below - // the margin ratio without replacing the price oracles. - it('invalidates a virtually insolvent order', async () => { - // This account has a collateralization ratio JUST above the - // minimum margin ratio, so it can only withdraw nearly zero maker tokens. - const accountId = randomAccountId(); - await depositAndWithdrawAsync(accountId, 1, 1 / (minMarginRatio + 3e-4)); - const order = createOrder({ - makerAssetData: encodeDydxBridgeAssetData({ - accountId, - depositRate: 0, - }), + it('validates a partially solvent order with an inadequate deposit', async () => { + // This account is not very well collateralized and the deposit rate is + // also too low to sustain the collateralization ratio for the full order. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: minMarginRatio * 0.95, + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + it('validates a partially solvent order with no deposit', async () => { + // This account is not very well collateralized and there is no deposit + // to keep the collateralization ratio up. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 0); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + expect(fillableTakerAssetAmount).to.bignumber.gt(0); + expect(fillableTakerAssetAmount).to.bignumber.lt(order.takerAssetAmount); + }); + + // TODO(dorothy-zbornak): We can't actually create an account that's below + // the margin ratio without replacing the price oracles. + it('invalidates a virtually insolvent order', async () => { + // This account has a collateralization ratio JUST above the + // minimum margin ratio, so it can only withdraw nearly zero maker tokens. + const accountId = randomAccountId(); + await depositAndWithdrawAsync(makerAddress, accountId, 1, 1 / (minMarginRatio + 3e-4)); + const order = _createOrder({ + makerAssetData: encodeDydxBridgeAssetData({ + accountId, + depositRate: 0, + fromToken: USDC_ADDRESS, + toToken: DAI_ADDRESS, + }), + }); + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); + // Price fluctuations will cause this to be a little above zero, so we + // don't compare to zero. + expect(fillableTakerAssetAmount).to.bignumber.lt(fromTokenUnitAmount(1e-3, USDC_DECIMALS)); }); - const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState(order, SIGNATURE).callAsync(); - // Price fluctuations will cause this to be a little above zero, so we - // don't compare to zero. - expect(fillableTakerAssetAmount).to.bignumber.lt(fromTokenUnitAmount(1e-7, DAI_DECIMALS)); }); }); From 3ec8924e7fb70364e41468b95ca74ece31b545ba Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 16:02:35 -0500 Subject: [PATCH 16/18] `@0x/contracts-dev-utils`: Rebase. --- .../contracts/src/OrderValidationUtils.sol | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index ea319c07ee..948e1d8138 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -118,22 +118,14 @@ contract OrderValidationUtils is transferableTakerAssetAmount ); - // Execute the maker transfers. - fillableTakerAssetAmount = LibOrderTransferSimulation.getSimulatedOrderMakerTransferResults( - exchangeAddress, - order, - order.takerAddress, - fillableTakerAssetAmount - ) == LibOrderTransferSimulation.OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0; - - if (!_isAssetDataValid(order.takerAssetData)) { - fillableTakerAssetAmount = 0; - } - - if (order.takerFee != 0 && !_isAssetDataValid(order.takerFeeAssetData)) { + // Ensure that all of the asset data is valid. Fee asset data only needs + // to be valid if the fees are nonzero. + if (!_areOrderAssetDatasValid(order)) { fillableTakerAssetAmount = 0; } + // If the order is not fillable, then the fillable taker asset amount is + // zero by definition. if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE) { fillableTakerAssetAmount = 0; } @@ -210,6 +202,21 @@ contract OrderValidationUtils is return LibSafeMath.min256(transferableAssetAmount, order.makerAssetAmount); } + /// @dev Checks that the asset data contained in a ZeroEx is valid and returns + /// a boolean that indicates whether or not the asset data was found to be valid. + /// @param order A ZeroEx order to validate. + /// @return The validatity of the asset data. + function _areOrderAssetDatasValid(LibOrder.Order memory order) + internal + pure + returns (bool) + { + return _isAssetDataValid(order.makerAssetData) && + (order.makerFee == 0 || _isAssetDataValid(order.makerFeeAssetData)) && + _isAssetDataValid(order.takerAssetData) && + (order.takerFee == 0 || _isAssetDataValid(order.takerFeeAssetData)); + } + /// @dev This function handles the edge cases around taker validation. This function /// currently attempts to find duplicate ERC721 token's in the taker /// multiAssetData. From 3da03da32a2b1d0a504bf8e10db387e45e216951 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 16:19:09 -0500 Subject: [PATCH 17/18] `@0x/contracts-utils`: Add `D18` library. --- contracts/utils/CHANGELOG.json | 9 + contracts/utils/contracts/src/D18.sol | 248 ++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 contracts/utils/contracts/src/D18.sol diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index 251907e9c5..38c9a0bfbe 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "4.4.0", + "changes": [ + { + "note": "Add `D18` library", + "pr": 2466 + } + ] + }, { "timestamp": 1581748629, "version": "4.3.1", diff --git a/contracts/utils/contracts/src/D18.sol b/contracts/utils/contracts/src/D18.sol new file mode 100644 index 0000000000..d6fde12b0f --- /dev/null +++ b/contracts/utils/contracts/src/D18.sol @@ -0,0 +1,248 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.16; + + +/// @dev A library for working with 18 digit, base 10 decimals. +library D18 { + + /// @dev Decimal places for dydx value quantities. + uint256 private constant PRECISION = 18; + /// @dev 1.0 in base-18 decimal. + int256 private constant DECIMAL_ONE = int256(10 ** PRECISION); + /// @dev Minimum signed integer value. + int256 private constant MIN_INT256_VALUE = int256(0x8000000000000000000000000000000000000000000000000000000000000000); + + /// @dev Return `1.0` + function one() + internal + pure + returns (int256 r) + { + r = DECIMAL_ONE; + } + + /// @dev Add two decimals. + function add(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, b); + } + + /// @dev Add two decimals. + function add(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), b); + } + + /// @dev Add two decimals. + function add(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(a, int256(b)); + } + + /// @dev Add two decimals. + function add(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), int256(b)); + } + + /// @dev Subract two decimals. + function sub(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _add(a, -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -b); + } + + /// @dev Subract two decimals. + function sub(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _add(int256(a), -int256(b)); + } + + /// @dev Multiply two decimals. + function mul(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _div(_mul(a, b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), b), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, int256(b)), DECIMAL_ONE); + } + + /// @dev Multiply two decimals. + function mul(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), int256(b)), DECIMAL_ONE); + } + + /// @dev Divide two decimals. + function div(int256 a, int256 b) + internal + pure + returns (int256 r) + { + r = _div(_mul(a, DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(uint256 a, int256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), b); + } + + /// @dev Divide two decimals. + function div(int256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(a, DECIMAL_ONE), int256(b)); + } + + /// @dev Divide two decimals. + function div(uint256 a, uint256 b) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = _div(_mul(int256(a), DECIMAL_ONE), int256(b)); + } + + /// @dev Safely convert an unsigned integer into a signed integer. + function toSigned(uint256 a) + internal + pure + returns (int256 r) + { + require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); + r = int256(a); + } + + /// @dev Clip a signed value to be positive. + function clip(int256 a) + internal + pure + returns (int256 r) + { + r = a < 0 ? 0 : a; + } + + /// @dev Safely multiply two signed integers. + function _mul(int256 a, int256 b) + private + pure + returns (int256 r) + { + if (a == 0 || b == 0) { + return 0; + } + r = a * b; + require(r / a == b && r / b == a, "D18/DECIMAL_MUL_OVERFLOW"); + return r; + } + + /// @dev Safely divide two signed integers. + function _div(int256 a, int256 b) + private + pure + returns (int256 r) + { + require(b != 0, "D18/DECIMAL_DIV_BY_ZERO"); + require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_DIV_OVERFLOW"); + r = a / b; + } + + /// @dev Safely add two signed integers. + function _add(int256 a, int256 b) + private + pure + returns (int256 r) + { + r = a + b; + require( + !((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)), + "D18/DECIMAL_ADD_OVERFLOW" + ); + } + +} From e851cb1cbcdda3dde53d423e85745aaea11e28a2 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 19 Feb 2020 16:19:38 -0500 Subject: [PATCH 18/18] `@0x/contracts-dev-utils`: Move `D18` out to `@0x/contracts-utils`. --- contracts/dev-utils/CHANGELOG.json | 17 +- contracts/dev-utils/contracts/src/D18.sol | 248 ------------------ .../contracts/src/LibDydxBalance.sol | 2 +- contracts/dev-utils/package.json | 2 +- contracts/dev-utils/test/artifacts.ts | 2 - contracts/dev-utils/test/wrappers.ts | 1 - contracts/dev-utils/tsconfig.json | 1 - 7 files changed, 11 insertions(+), 262 deletions(-) delete mode 100644 contracts/dev-utils/contracts/src/D18.sol diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index 03fa998383..a97f1cfe02 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.2.0", + "changes": [ + { + "note": "Add `DydxBridge` order validation", + "pr": 2466 + } + ] + }, { "timestamp": 1581748629, "version": "1.1.1", @@ -18,14 +27,6 @@ { "note": "Remove `LibTransactionDecoder` export", "pr": 2464 - }, - { - "note": "Add `DydxBridge` order validation", - "pr": 2466 - }, - { - "note": "Add `D18` library for working with base-10, 18-precision decimals", - "pr": 2466 } ], "timestamp": 1581204851 diff --git a/contracts/dev-utils/contracts/src/D18.sol b/contracts/dev-utils/contracts/src/D18.sol deleted file mode 100644 index d6fde12b0f..0000000000 --- a/contracts/dev-utils/contracts/src/D18.sol +++ /dev/null @@ -1,248 +0,0 @@ -/* - - Copyright 2019 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.5.16; - - -/// @dev A library for working with 18 digit, base 10 decimals. -library D18 { - - /// @dev Decimal places for dydx value quantities. - uint256 private constant PRECISION = 18; - /// @dev 1.0 in base-18 decimal. - int256 private constant DECIMAL_ONE = int256(10 ** PRECISION); - /// @dev Minimum signed integer value. - int256 private constant MIN_INT256_VALUE = int256(0x8000000000000000000000000000000000000000000000000000000000000000); - - /// @dev Return `1.0` - function one() - internal - pure - returns (int256 r) - { - r = DECIMAL_ONE; - } - - /// @dev Add two decimals. - function add(int256 a, int256 b) - internal - pure - returns (int256 r) - { - r = _add(a, b); - } - - /// @dev Add two decimals. - function add(uint256 a, int256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _add(int256(a), b); - } - - /// @dev Add two decimals. - function add(int256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _add(a, int256(b)); - } - - /// @dev Add two decimals. - function add(uint256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _add(int256(a), int256(b)); - } - - /// @dev Subract two decimals. - function sub(int256 a, int256 b) - internal - pure - returns (int256 r) - { - r = _add(a, -b); - } - - /// @dev Subract two decimals. - function sub(uint256 a, int256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _add(int256(a), -b); - } - - /// @dev Subract two decimals. - function sub(uint256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _add(int256(a), -int256(b)); - } - - /// @dev Multiply two decimals. - function mul(int256 a, int256 b) - internal - pure - returns (int256 r) - { - r = _div(_mul(a, b), DECIMAL_ONE); - } - - /// @dev Multiply two decimals. - function mul(uint256 a, int256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(int256(a), b), DECIMAL_ONE); - } - - /// @dev Multiply two decimals. - function mul(int256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(a, int256(b)), DECIMAL_ONE); - } - - /// @dev Multiply two decimals. - function mul(uint256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(int256(a), int256(b)), DECIMAL_ONE); - } - - /// @dev Divide two decimals. - function div(int256 a, int256 b) - internal - pure - returns (int256 r) - { - r = _div(_mul(a, DECIMAL_ONE), b); - } - - /// @dev Divide two decimals. - function div(uint256 a, int256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(int256(a), DECIMAL_ONE), b); - } - - /// @dev Divide two decimals. - function div(int256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(a, DECIMAL_ONE), int256(b)); - } - - /// @dev Divide two decimals. - function div(uint256 a, uint256 b) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - require(int256(b) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = _div(_mul(int256(a), DECIMAL_ONE), int256(b)); - } - - /// @dev Safely convert an unsigned integer into a signed integer. - function toSigned(uint256 a) - internal - pure - returns (int256 r) - { - require(int256(a) >= 0, "D18/DECIMAL_VALUE_TOO_BIG"); - r = int256(a); - } - - /// @dev Clip a signed value to be positive. - function clip(int256 a) - internal - pure - returns (int256 r) - { - r = a < 0 ? 0 : a; - } - - /// @dev Safely multiply two signed integers. - function _mul(int256 a, int256 b) - private - pure - returns (int256 r) - { - if (a == 0 || b == 0) { - return 0; - } - r = a * b; - require(r / a == b && r / b == a, "D18/DECIMAL_MUL_OVERFLOW"); - return r; - } - - /// @dev Safely divide two signed integers. - function _div(int256 a, int256 b) - private - pure - returns (int256 r) - { - require(b != 0, "D18/DECIMAL_DIV_BY_ZERO"); - require(a != MIN_INT256_VALUE || b != -1, "D18/DECIMAL_DIV_OVERFLOW"); - r = a / b; - } - - /// @dev Safely add two signed integers. - function _add(int256 a, int256 b) - private - pure - returns (int256 r) - { - r = a + b; - require( - !((a < 0 && b < 0 && r > a) || (a > 0 && b > 0 && r < a)), - "D18/DECIMAL_ADD_OVERFLOW" - ); - } - -} diff --git a/contracts/dev-utils/contracts/src/LibDydxBalance.sol b/contracts/dev-utils/contracts/src/LibDydxBalance.sol index 8718f5368c..4ca815e4b8 100644 --- a/contracts/dev-utils/contracts/src/LibDydxBalance.sol +++ b/contracts/dev-utils/contracts/src/LibDydxBalance.sol @@ -26,8 +26,8 @@ import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; +import "@0x/contracts-utils/contracts/src/D18.sol"; import "./LibAssetData.sol"; -import "./D18.sol"; library LibDydxBalance { diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index 3dd5b99b23..b3cf58b3ee 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -28,7 +28,7 @@ }, "config": { "publicInterfaceContracts": "DevUtils,LibAssetData,LibDydxBalance,LibOrderTransferSimulation,LibTransactionDecoder", - "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|D18|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", + "abis": "./test/generated-artifacts/@(Addresses|AssetBalance|DevUtils|EthBalanceChecker|ExternalFunctions|LibAssetData|LibDydxBalance|LibOrderTransferSimulation|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils|TestDydx|TestLibDydxBalance).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/test/artifacts.ts b/contracts/dev-utils/test/artifacts.ts index a4ff20758e..e4c1802bc0 100644 --- a/contracts/dev-utils/test/artifacts.ts +++ b/contracts/dev-utils/test/artifacts.ts @@ -7,7 +7,6 @@ import { ContractArtifact } from 'ethereum-types'; import * as Addresses from '../test/generated-artifacts/Addresses.json'; import * as AssetBalance from '../test/generated-artifacts/AssetBalance.json'; -import * as D18 from '../test/generated-artifacts/D18.json'; import * as DevUtils from '../test/generated-artifacts/DevUtils.json'; import * as EthBalanceChecker from '../test/generated-artifacts/EthBalanceChecker.json'; import * as ExternalFunctions from '../test/generated-artifacts/ExternalFunctions.json'; @@ -22,7 +21,6 @@ import * as TestLibDydxBalance from '../test/generated-artifacts/TestLibDydxBala export const artifacts = { Addresses: Addresses as ContractArtifact, AssetBalance: AssetBalance as ContractArtifact, - D18: D18 as ContractArtifact, DevUtils: DevUtils as ContractArtifact, EthBalanceChecker: EthBalanceChecker as ContractArtifact, ExternalFunctions: ExternalFunctions as ContractArtifact, diff --git a/contracts/dev-utils/test/wrappers.ts b/contracts/dev-utils/test/wrappers.ts index 587e4c6281..01f291fa09 100644 --- a/contracts/dev-utils/test/wrappers.ts +++ b/contracts/dev-utils/test/wrappers.ts @@ -5,7 +5,6 @@ */ export * from '../test/generated-wrappers/addresses'; export * from '../test/generated-wrappers/asset_balance'; -export * from '../test/generated-wrappers/d18'; export * from '../test/generated-wrappers/dev_utils'; export * from '../test/generated-wrappers/eth_balance_checker'; export * from '../test/generated-wrappers/external_functions'; diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index b2b229e0c7..eccc9bf633 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -10,7 +10,6 @@ "generated-artifacts/LibTransactionDecoder.json", "test/generated-artifacts/Addresses.json", "test/generated-artifacts/AssetBalance.json", - "test/generated-artifacts/D18.json", "test/generated-artifacts/DevUtils.json", "test/generated-artifacts/EthBalanceChecker.json", "test/generated-artifacts/ExternalFunctions.json",