diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index b6ef247c7f..c2cc1a6d47 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -1,10 +1,14 @@ [ { - "version": "3.3.1", + "version": "3.4.0", "changes": [ { "note": "Fix instability with DFB.", "pr": 2616 + }, + { + "note": "Add `BalancerBridge`", + "pr": 2613 } ] }, diff --git a/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol b/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol new file mode 100644 index 0000000000..ef75c586fc --- /dev/null +++ b/contracts/asset-proxy/contracts/src/bridges/BalancerBridge.sol @@ -0,0 +1,103 @@ + +/* + + Copyright 2020 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.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; +import "../interfaces/IERC20Bridge.sol"; +import "../interfaces/IBalancerPool.sol"; + + +contract BalancerBridge is + IERC20Bridge, + IWallet, + DeploymentConstants +{ + /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of + /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` + /// token encoded in the bridge data, then transfers the bought + /// tokens to `to`. + /// @param toTokenAddress The token to buy and transfer to `to`. + /// @param from The maker (this contract). + /// @param to The recipient of the bought tokens. + /// @param amount Minimum amount of `toTokenAddress` tokens to buy. + /// @param bridgeData The abi-encoded addresses of the "from" token and Balancer pool. + /// @return success The magic bytes if successful. + function bridgeTransferFrom( + address toTokenAddress, + address from, + address to, + uint256 amount, + bytes calldata bridgeData + ) + external + returns (bytes4 success) + { + // Decode the bridge data. + (address fromTokenAddress, address poolAddress) = abi.decode( + bridgeData, + (address, address) + ); + require(toTokenAddress != fromTokenAddress, "BalancerBridge/INVALID_PAIR"); + + uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); + // Grant an allowance to the exchange to spend `fromTokenAddress` token. + LibERC20Token.approveIfBelow(fromTokenAddress, poolAddress, fromTokenBalance); + + // Sell all of this contract's `fromTokenAddress` token balance. + (uint256 boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn( + fromTokenAddress, // tokenIn + fromTokenBalance, // tokenAmountIn + toTokenAddress, // tokenOut + amount, // minAmountOut + uint256(-1) // maxPrice + ); + + // Transfer the converted `toToken`s to `to`. + LibERC20Token.transfer(toTokenAddress, to, boughtAmount); + + emit ERC20BridgeTransfer( + fromTokenAddress, + toTokenAddress, + fromTokenBalance, + boughtAmount, + from, + to + ); + return BRIDGE_SUCCESS; + } + + /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker + /// and sign for itself in orders. Always succeeds. + /// @return magicValue Magic success bytes, always. + function isValidSignature( + bytes32, + bytes calldata + ) + external + view + returns (bytes4 magicValue) + { + return LEGACY_WALLET_MAGIC_VALUE; + } +} diff --git a/contracts/asset-proxy/contracts/src/interfaces/IBalancerPool.sol b/contracts/asset-proxy/contracts/src/interfaces/IBalancerPool.sol new file mode 100644 index 0000000000..8db7e0d7ce --- /dev/null +++ b/contracts/asset-proxy/contracts/src/interfaces/IBalancerPool.sol @@ -0,0 +1,39 @@ +/* + + Copyright 2020 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.9; + + +interface IBalancerPool { + /// @dev Sell `tokenAmountIn` of `tokenIn` and receive `tokenOut`. + /// @param tokenIn The token being sold + /// @param tokenAmountIn The amount of `tokenIn` to sell. + /// @param tokenOut The token being bought. + /// @param minAmountOut The minimum amount of `tokenOut` to buy. + /// @param maxPrice The maximum value for `spotPriceAfter`. + /// @return tokenAmountOut The amount of `tokenOut` bought. + /// @return spotPriceAfter The new marginal spot price of the given + /// token pair for this pool. + function swapExactAmountIn( + address tokenIn, + uint tokenAmountIn, + address tokenOut, + uint minAmountOut, + uint maxPrice + ) external returns (uint tokenAmountOut, uint spotPriceAfter); +} \ No newline at end of file diff --git a/contracts/asset-proxy/package.json b/contracts/asset-proxy/package.json index 888923d8d2..982199c4f6 100644 --- a/contracts/asset-proxy/package.json +++ b/contracts/asset-proxy/package.json @@ -38,7 +38,7 @@ "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { - "abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", + "abis": "./test/generated-artifacts/@(BalancerBridge|ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/asset-proxy/src/artifacts.ts b/contracts/asset-proxy/src/artifacts.ts index 768e514e28..f9fba6001f 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -5,6 +5,7 @@ */ import { ContractArtifact } from 'ethereum-types'; +import * as BalancerBridge from '../generated-artifacts/BalancerBridge.json'; import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json'; import * as CurveBridge from '../generated-artifacts/CurveBridge.json'; import * as DexForwarderBridge from '../generated-artifacts/DexForwarderBridge.json'; @@ -18,6 +19,7 @@ import * as IAssetData from '../generated-artifacts/IAssetData.json'; import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json'; import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json'; import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json'; +import * as IBalancerPool from '../generated-artifacts/IBalancerPool.json'; import * as IChai from '../generated-artifacts/IChai.json'; import * as ICurve from '../generated-artifacts/ICurve.json'; import * as IDydx from '../generated-artifacts/IDydx.json'; @@ -57,6 +59,7 @@ export const artifacts = { ERC721Proxy: ERC721Proxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact, + BalancerBridge: BalancerBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, CurveBridge: CurveBridge as ContractArtifact, DexForwarderBridge: DexForwarderBridge as ContractArtifact, @@ -70,6 +73,7 @@ export const artifacts = { IAssetProxy: IAssetProxy as ContractArtifact, IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact, + IBalancerPool: IBalancerPool as ContractArtifact, IChai: IChai as ContractArtifact, ICurve: ICurve as ContractArtifact, IDydx: IDydx as ContractArtifact, diff --git a/contracts/asset-proxy/src/index.ts b/contracts/asset-proxy/src/index.ts index 5f36e5e26b..b95851265b 100644 --- a/contracts/asset-proxy/src/index.ts +++ b/contracts/asset-proxy/src/index.ts @@ -1,5 +1,6 @@ export { artifacts } from './artifacts'; export { + BalancerBridgeContract, ChaiBridgeContract, ERC1155ProxyContract, ERC20BridgeProxyContract, diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index 1ad10f45db..d28a8871da 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -3,6 +3,7 @@ * Warning: This file is auto-generated by contracts-gen. Don't edit manually. * ----------------------------------------------------------------------------- */ +export * from '../generated-wrappers/balancer_bridge'; export * from '../generated-wrappers/chai_bridge'; export * from '../generated-wrappers/curve_bridge'; export * from '../generated-wrappers/dex_forwarder_bridge'; @@ -16,6 +17,7 @@ export * from '../generated-wrappers/i_asset_data'; export * from '../generated-wrappers/i_asset_proxy'; export * from '../generated-wrappers/i_asset_proxy_dispatcher'; export * from '../generated-wrappers/i_authorizable'; +export * from '../generated-wrappers/i_balancer_pool'; export * from '../generated-wrappers/i_chai'; export * from '../generated-wrappers/i_curve'; export * from '../generated-wrappers/i_dydx'; diff --git a/contracts/asset-proxy/test/artifacts.ts b/contracts/asset-proxy/test/artifacts.ts index 6b57210d8a..da9f7da1e6 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -5,6 +5,7 @@ */ import { ContractArtifact } from 'ethereum-types'; +import * as BalancerBridge from '../test/generated-artifacts/BalancerBridge.json'; import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json'; import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json'; import * as DexForwarderBridge from '../test/generated-artifacts/DexForwarderBridge.json'; @@ -18,6 +19,7 @@ import * as IAssetData from '../test/generated-artifacts/IAssetData.json'; import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json'; import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json'; import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json'; +import * as IBalancerPool from '../test/generated-artifacts/IBalancerPool.json'; import * as IChai from '../test/generated-artifacts/IChai.json'; import * as ICurve from '../test/generated-artifacts/ICurve.json'; import * as IDydx from '../test/generated-artifacts/IDydx.json'; @@ -57,6 +59,7 @@ export const artifacts = { ERC721Proxy: ERC721Proxy as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, StaticCallProxy: StaticCallProxy as ContractArtifact, + BalancerBridge: BalancerBridge as ContractArtifact, ChaiBridge: ChaiBridge as ContractArtifact, CurveBridge: CurveBridge as ContractArtifact, DexForwarderBridge: DexForwarderBridge as ContractArtifact, @@ -70,6 +73,7 @@ export const artifacts = { IAssetProxy: IAssetProxy as ContractArtifact, IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact, IAuthorizable: IAuthorizable as ContractArtifact, + IBalancerPool: IBalancerPool as ContractArtifact, IChai: IChai as ContractArtifact, ICurve: ICurve as ContractArtifact, IDydx: IDydx as ContractArtifact, diff --git a/contracts/asset-proxy/test/wrappers.ts b/contracts/asset-proxy/test/wrappers.ts index a53cb31833..21ff5057b2 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -3,6 +3,7 @@ * Warning: This file is auto-generated by contracts-gen. Don't edit manually. * ----------------------------------------------------------------------------- */ +export * from '../test/generated-wrappers/balancer_bridge'; export * from '../test/generated-wrappers/chai_bridge'; export * from '../test/generated-wrappers/curve_bridge'; export * from '../test/generated-wrappers/dex_forwarder_bridge'; @@ -16,6 +17,7 @@ export * from '../test/generated-wrappers/i_asset_data'; export * from '../test/generated-wrappers/i_asset_proxy'; export * from '../test/generated-wrappers/i_asset_proxy_dispatcher'; export * from '../test/generated-wrappers/i_authorizable'; +export * from '../test/generated-wrappers/i_balancer_pool'; export * from '../test/generated-wrappers/i_chai'; export * from '../test/generated-wrappers/i_curve'; export * from '../test/generated-wrappers/i_dydx'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index cc10ff7b0c..bfebf7aa84 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "files": [ + "generated-artifacts/BalancerBridge.json", "generated-artifacts/ChaiBridge.json", "generated-artifacts/CurveBridge.json", "generated-artifacts/DexForwarderBridge.json", @@ -16,6 +17,7 @@ "generated-artifacts/IAssetProxy.json", "generated-artifacts/IAssetProxyDispatcher.json", "generated-artifacts/IAuthorizable.json", + "generated-artifacts/IBalancerPool.json", "generated-artifacts/IChai.json", "generated-artifacts/ICurve.json", "generated-artifacts/IDydx.json", @@ -45,6 +47,7 @@ "generated-artifacts/TestUniswapV2Bridge.json", "generated-artifacts/UniswapBridge.json", "generated-artifacts/UniswapV2Bridge.json", + "test/generated-artifacts/BalancerBridge.json", "test/generated-artifacts/ChaiBridge.json", "test/generated-artifacts/CurveBridge.json", "test/generated-artifacts/DexForwarderBridge.json", @@ -58,6 +61,7 @@ "test/generated-artifacts/IAssetProxy.json", "test/generated-artifacts/IAssetProxyDispatcher.json", "test/generated-artifacts/IAuthorizable.json", + "test/generated-artifacts/IBalancerPool.json", "test/generated-artifacts/IChai.json", "test/generated-artifacts/ICurve.json", "test/generated-artifacts/IDydx.json", diff --git a/contracts/integrations/CHANGELOG.json b/contracts/integrations/CHANGELOG.json index 00910c4dc8..2a893d1528 100644 --- a/contracts/integrations/CHANGELOG.json +++ b/contracts/integrations/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.6.0", + "changes": [ + { + "note": "Add `BalancerBridge` mainnet tests", + "pr": 2613 + } + ] + }, { "version": "2.5.2", "changes": [ diff --git a/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts b/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts new file mode 100644 index 0000000000..270062624f --- /dev/null +++ b/contracts/integrations/test/bridges/balancer_bridge_mainnet_test.ts @@ -0,0 +1,103 @@ +import { artifacts as assetProxyArtifacts } from '@0x/contracts-asset-proxy'; +import { BalancerBridgeContract } from '@0x/contracts-asset-proxy/lib/src/wrappers'; +import { ERC20TokenContract } from '@0x/contracts-erc20'; +import { blockchainTests, constants, expect, toBaseUnitAmount } from '@0x/contracts-test-utils'; +import { AbiEncoder } from '@0x/utils'; + +const CHONKY_DAI_WALLET = '0x1e0447b19bb6ecfdae1e4ae1694b0c3659614e4e'; // dydx solo margin +const CHONKY_WETH_WALLET = '0x2f0b23f53734252bda2277357e97e1517d6b042a'; // MCD wETH vault +const CHONKY_USDC_WALLET = '0x39aa39c021dfbae8fac545936693ac917d5e7563'; // Compound +blockchainTests.configure({ + fork: { + unlockedAccounts: [CHONKY_USDC_WALLET, CHONKY_WETH_WALLET, CHONKY_DAI_WALLET], + }, +}); + +blockchainTests.fork('Mainnet Balancer bridge tests', env => { + let testContract: BalancerBridgeContract; + let weth: ERC20TokenContract; + let usdc: ERC20TokenContract; + const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308'; + const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; + const wethAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + const wethUsdcBalancerAddress = '0x2471de1547296aadb02cc1af84afe369b6f67c87'; + const wethUsdcDaiBalancerAddress = '0x9b208194acc0a8ccb2a8dcafeacfbb7dcc093f81'; + const bridgeDataEncoder = AbiEncoder.create([ + { name: 'takerToken', type: 'address' }, + { name: 'poolAddress', type: 'address' }, + ]); + + before(async () => { + testContract = await BalancerBridgeContract.deployFrom0xArtifactAsync( + assetProxyArtifacts.BalancerBridge, + env.provider, + { ...env.txDefaults, from: CHONKY_DAI_WALLET, gasPrice: 0 }, + {}, + ); + weth = new ERC20TokenContract(wethAddress, env.provider, env.txDefaults); + usdc = new ERC20TokenContract(usdcAddress, env.provider, env.txDefaults); + }); + + blockchainTests.resets('Can trade with two-asset pool', () => { + it('successfully exchanges WETH for USDC', async () => { + const bridgeData = bridgeDataEncoder.encode([wethAddress, wethUsdcBalancerAddress]); + // Fund the Bridge + await weth + .transfer(testContract.address, toBaseUnitAmount(1)) + .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const usdcBalanceBefore = await usdc.balanceOf(receiver).callAsync(); + // Exchange via Balancer + await testContract + .bridgeTransferFrom(usdcAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) + .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + // Check that USDC balance increased + const usdcBalanceAfter = await usdc.balanceOf(receiver).callAsync(); + expect(usdcBalanceAfter).to.be.bignumber.greaterThan(usdcBalanceBefore); + }); + it('successfully exchanges USDC for WETH', async () => { + const bridgeData = bridgeDataEncoder.encode([usdcAddress, wethUsdcBalancerAddress]); + // Fund the Bridge + await usdc + .transfer(testContract.address, toBaseUnitAmount(1, 6)) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceBefore = await weth.balanceOf(receiver).callAsync(); + // Exchange via Balancer + await testContract + .bridgeTransferFrom(wethAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceAfter = await weth.balanceOf(receiver).callAsync(); + expect(wethBalanceAfter).to.be.bignumber.greaterThan(wethBalanceBefore); + }); + }); + blockchainTests.resets('Can trade with three-asset pool', () => { + it('successfully exchanges WETH for USDC', async () => { + const bridgeData = bridgeDataEncoder.encode([wethAddress, wethUsdcDaiBalancerAddress]); + // Fund the Bridge + await weth + .transfer(testContract.address, toBaseUnitAmount(1)) + .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const usdcBalanceBefore = await usdc.balanceOf(receiver).callAsync(); + // Exchange via Balancer + await testContract + .bridgeTransferFrom(usdcAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) + .awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); + // Check that USDC balance increased + const usdcBalanceAfter = await usdc.balanceOf(receiver).callAsync(); + expect(usdcBalanceAfter).to.be.bignumber.greaterThan(usdcBalanceBefore); + }); + it('successfully exchanges USDC for WETH', async () => { + const bridgeData = bridgeDataEncoder.encode([usdcAddress, wethUsdcDaiBalancerAddress]); + // Fund the Bridge + await usdc + .transfer(testContract.address, toBaseUnitAmount(1, 6)) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceBefore = await weth.balanceOf(receiver).callAsync(); + // Exchange via Balancer + await testContract + .bridgeTransferFrom(wethAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) + .awaitTransactionSuccessAsync({ from: CHONKY_USDC_WALLET, gasPrice: 0 }, { shouldValidate: false }); + const wethBalanceAfter = await weth.balanceOf(receiver).callAsync(); + expect(wethBalanceAfter).to.be.bignumber.greaterThan(wethBalanceBefore); + }); + }); +}); diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 8424fa6933..86b31d43b8 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -25,6 +25,18 @@ { "note": "\"Fix\" forwarder buys of low decimal tokens.", "pr": 2618 + }, + { + "note": "Add Balancer support", + "pr": 2613 + }, + { + "note": "Consolidate UniswapV2 sources, Curve sources in `ERC20BridgeSource` enum", + "pr": 2613 + }, + { + "note": "Change gas/fee schedule values from constants to functions returning numbers", + "pr": 2613 } ] }, diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 0b2a602f57..fa371d1892 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -55,8 +55,10 @@ "@0x/quote-server": "^2.0.2", "@0x/utils": "^5.5.0", "@0x/web3-wrapper": "^7.1.0", + "@balancer-labs/sor": "^0.3.0", "axios": "^0.19.2", "axios-mock-adapter": "^1.18.1", + "decimal.js": "^10.2.0", "ethereumjs-util": "^5.1.1", "heartbeats": "^5.0.1", "lodash": "^4.17.11" diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index d3000b8366..16e58a6d07 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -69,6 +69,12 @@ export { NativeCollapsedFill, OptimizedMarketOrder, GetMarketOrdersRfqtOpts, + FeeSchedule, + FillData, + NativeFillData, + CurveFillData, + BalancerFillData, + UniswapV2FillData, } from './utils/market_operation_utils/types'; export { affiliateFeeUtils } from './utils/affiliate_fee_utils'; export { ProtocolFeeUtils } from './utils/protocol_fee_utils'; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts new file mode 100644 index 0000000000..e412172a53 --- /dev/null +++ b/packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts @@ -0,0 +1,102 @@ +import { BigNumber } from '@0x/utils'; +import { bmath, getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor'; +import { Decimal } from 'decimal.js'; +import * as _ from 'lodash'; + +export interface BalancerPool { + id: string; + balanceIn: BigNumber; + balanceOut: BigNumber; + weightIn: BigNumber; + weightOut: BigNumber; + swapFee: BigNumber; + spotPrice?: BigNumber; + slippage?: BigNumber; + limitAmount?: BigNumber; +} + +interface CacheValue { + timestamp: number; + pools: BalancerPool[]; +} + +// tslint:disable:custom-no-magic-numbers +const FIVE_SECONDS_MS = 5 * 1000; +const DEFAULT_TIMEOUT_MS = 1000; +const MAX_POOLS_FETCHED = 3; +const Decimal20 = Decimal.clone({ precision: 20 }); +// tslint:enable:custom-no-magic-numbers + +export class BalancerPoolsCache { + constructor( + private readonly _cache: { [key: string]: CacheValue } = {}, + public cacheExpiryMs: number = FIVE_SECONDS_MS, + ) {} + + public async getPoolsForPairAsync( + takerToken: string, + makerToken: string, + timeoutMs: number = DEFAULT_TIMEOUT_MS, + ): Promise { + const timeout = new Promise(resolve => setTimeout(resolve, timeoutMs, [])); + return Promise.race([this._getPoolsForPairAsync(takerToken, makerToken), timeout]); + } + + protected async _getPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + const key = JSON.stringify([takerToken, makerToken]); + const value = this._cache[key]; + const minTimestamp = Date.now() - this.cacheExpiryMs; + if (value === undefined || value.timestamp < minTimestamp) { + const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); + const timestamp = Date.now(); + this._cache[key] = { + pools, + timestamp, + }; + } + return this._cache[key].pools; + } + + // tslint:disable-next-line:prefer-function-over-method + protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + try { + const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools; + // Sort by maker token balance (descending) + const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) => + b.balanceOut.minus(a.balanceOut).toNumber(), + ); + return pools.length > MAX_POOLS_FETCHED ? pools.slice(0, MAX_POOLS_FETCHED) : pools; + } catch (err) { + return []; + } + } +} + +// tslint:disable completed-docs +export function computeBalancerSellQuote(pool: BalancerPool, takerFillAmount: BigNumber): BigNumber { + const weightRatio = pool.weightIn.dividedBy(pool.weightOut); + const adjustedIn = bmath.BONE.minus(pool.swapFee) + .dividedBy(bmath.BONE) + .times(takerFillAmount); + const y = pool.balanceIn.dividedBy(pool.balanceIn.plus(adjustedIn)); + const foo = Math.pow(y.toNumber(), weightRatio.toNumber()); + const bar = new BigNumber(1).minus(foo); + const tokenAmountOut = pool.balanceOut.times(bar); + return tokenAmountOut.integerValue(); +} + +export function computeBalancerBuyQuote(pool: BalancerPool, makerFillAmount: BigNumber): BigNumber { + if (makerFillAmount.isGreaterThanOrEqualTo(pool.balanceOut)) { + return new BigNumber(0); + } + const weightRatio = pool.weightOut.dividedBy(pool.weightIn); + const diff = pool.balanceOut.minus(makerFillAmount); + const y = pool.balanceOut.dividedBy(diff); + let foo: number | Decimal = Math.pow(y.toNumber(), weightRatio.toNumber()) - 1; + if (!Number.isFinite(foo)) { + foo = new Decimal20(y.toString()).pow(weightRatio.toString()).minus(1); + } + let tokenAmountIn = bmath.BONE.minus(pool.swapFee).dividedBy(bmath.BONE); + tokenAmountIn = pool.balanceIn.times(foo.toString()).dividedBy(tokenAmountIn); + return tokenAmountIn.integerValue(); +} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 346fbc85a7..4fec2a348c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -10,15 +10,10 @@ import { ERC20BridgeSource, FakeBuyOpts, GetMarketOrdersOpts } from './types'; export const SELL_SOURCES = [ ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV2Eth, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber, - // All Curve Sources - ERC20BridgeSource.CurveUsdcDai, - ERC20BridgeSource.CurveUsdcDaiUsdt, - ERC20BridgeSource.CurveUsdcDaiUsdtTusd, - ERC20BridgeSource.CurveUsdcDaiUsdtBusd, - ERC20BridgeSource.CurveUsdcDaiUsdtSusd, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, ]; /** @@ -27,15 +22,10 @@ export const SELL_SOURCES = [ export const BUY_SOURCES = [ ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.UniswapV2Eth, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber, - // All Curve sources - ERC20BridgeSource.CurveUsdcDai, - ERC20BridgeSource.CurveUsdcDaiUsdt, - ERC20BridgeSource.CurveUsdcDaiUsdtBusd, - ERC20BridgeSource.CurveUsdcDaiUsdtTusd, - ERC20BridgeSource.CurveUsdcDaiUsdtSusd, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, ]; export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { @@ -60,56 +50,44 @@ export const DEFAULT_FAKE_BUY_OPTS: FakeBuyOpts = { /** * Sources to poll for ETH fee price estimates. */ -export const FEE_QUOTE_SOURCES = SELL_SOURCES; +export const FEE_QUOTE_SOURCES = [ + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Eth2Dai, + ERC20BridgeSource.Kyber, +]; /** * Mainnet Curve configuration */ -export const DEFAULT_CURVE_OPTS: { [source: string]: { version: number; curveAddress: string; tokens: string[] } } = { - [ERC20BridgeSource.CurveUsdcDai]: { - version: 1, - curveAddress: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', - tokens: ['0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'], - }, - [ERC20BridgeSource.CurveUsdcDaiUsdt]: { - version: 1, - curveAddress: '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c', - tokens: [ - '0x6b175474e89094c44da98b954eedeac495271d0f', - '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - '0xdac17f958d2ee523a2206206994597c13d831ec7', - ], - }, - [ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: { - version: 1, - curveAddress: '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51', - tokens: [ - '0x6b175474e89094c44da98b954eedeac495271d0f', - '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - '0xdac17f958d2ee523a2206206994597c13d831ec7', - '0x0000000000085d4780b73119b644ae5ecd22b376', - ], - }, - [ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: { - version: 1, - curveAddress: '0x79a8c46dea5ada233abaffd40f3a0a2b1e5a4f27', - tokens: [ - '0x6b175474e89094c44da98b954eedeac495271d0f', - '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - '0xdac17f958d2ee523a2206206994597c13d831ec7', - '0x4fabb145d64652a948d72533023f6e7a623c7c53', - ], - }, - [ERC20BridgeSource.CurveUsdcDaiUsdtSusd]: { - version: 1, - curveAddress: '0xa5407eae9ba41422680e2e00537571bcc53efbfd', - tokens: [ - '0x6b175474e89094c44da98b954eedeac495271d0f', - '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - '0xdac17f958d2ee523a2206206994597c13d831ec7', - '0x57ab1ec28d129707052df4df418d58a2d46d5f51', - ], - }, +export const MAINNET_CURVE_CONTRACTS: { [curveAddress: string]: string[] } = { + '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56': [ + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + ], + '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c': [ + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + ], + '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51': [ + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x0000000000085d4780b73119b644ae5ecd22b376', // TUSD + ], + '0x79a8c46dea5ada233abaffd40f3a0a2b1e5a4f27': [ + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x4fabb145d64652a948d72533023f6e7a623c7c53', // BUSD + ], + '0xa5407eae9ba41422680e2e00537571bcc53efbfd': [ + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x57ab1ec28d129707052df4df418d58a2d46d5f51', // SUSD + ], }; export const ERC20_PROXY_ID = '0xf47261b0'; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts new file mode 100644 index 0000000000..2af692acfe --- /dev/null +++ b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts @@ -0,0 +1,8 @@ +import { MAINNET_CURVE_CONTRACTS } from './constants'; + +// tslint:disable completed-docs +export function getCurveAddressesForPair(takerToken: string, makerToken: string): string[] { + return Object.keys(MAINNET_CURVE_CONTRACTS).filter(a => + [makerToken, takerToken].every(t => MAINNET_CURVE_CONTRACTS[a].includes(t)), + ); +} diff --git a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts b/packages/asset-swapper/src/utils/market_operation_utils/fills.ts index cfab8b4796..6908eca747 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/fills.ts @@ -4,15 +4,7 @@ import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types'; import { fillableAmountsUtils } from '../../utils/fillable_amounts_utils'; import { POSITIVE_INF, ZERO_AMOUNT } from './constants'; -import { - CollapsedFill, - DexSample, - ERC20BridgeSource, - Fill, - FillFlags, - NativeCollapsedFill, - NativeFillData, -} from './types'; +import { CollapsedFill, DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillFlags } from './types'; // tslint:disable: prefer-for-of no-bitwise completed-docs @@ -26,7 +18,7 @@ export function createFillPaths(opts: { targetInput?: BigNumber; ethToOutputRate?: BigNumber; excludedSources?: ERC20BridgeSource[]; - feeSchedule?: { [source: string]: BigNumber }; + feeSchedule?: FeeSchedule; }): Fill[][] { const { side } = opts; const excludedSources = opts.excludedSources || []; @@ -62,7 +54,7 @@ function nativeOrdersToPath( orders: SignedOrderWithFillableAmounts[], targetInput: BigNumber = POSITIVE_INF, ethToOutputRate: BigNumber, - fees: { [source: string]: BigNumber }, + fees: FeeSchedule, ): Fill[] { // Create a single path from all orders. let path: Fill[] = []; @@ -71,7 +63,9 @@ function nativeOrdersToPath( const takerAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterOrderFees(order); const input = side === MarketOperation.Sell ? takerAmount : makerAmount; const output = side === MarketOperation.Sell ? makerAmount : takerAmount; - const penalty = ethToOutputRate.times(fees[ERC20BridgeSource.Native] || 0); + const penalty = ethToOutputRate.times( + fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(), + ); const rate = makerAmount.div(takerAmount); // targetInput can be less than the order size // whilst the penalty is constant, it affects the adjusted output @@ -116,7 +110,7 @@ function dexQuotesToPaths( side: MarketOperation, dexQuotes: DexSample[][], ethToOutputRate: BigNumber, - fees: { [source: string]: BigNumber }, + fees: FeeSchedule, ): Fill[][] { const paths: Fill[][] = []; for (let quote of dexQuotes) { @@ -129,12 +123,13 @@ function dexQuotesToPaths( for (let i = 0; i < quote.length; i++) { const sample = quote[i]; const prevSample = i === 0 ? undefined : quote[i - 1]; - const source = sample.source; + const { source, fillData } = sample; const input = sample.input.minus(prevSample ? prevSample.input : 0); const output = sample.output.minus(prevSample ? prevSample.output : 0); + const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData); const penalty = i === 0 // Only the first fill in a DEX path incurs a penalty. - ? ethToOutputRate.times(fees[source] || 0) + ? ethToOutputRate.times(fee) : ZERO_AMOUNT; const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty); const rate = side === MarketOperation.Sell ? output.div(input) : input.div(output); @@ -147,6 +142,7 @@ function dexQuotesToPaths( adjustedRate, adjustedOutput, source, + fillData, index: i, parent: i !== 0 ? path[path.length - 1] : undefined, flags: sourceToFillFlags(source), @@ -241,7 +237,7 @@ export function clipPathToInput(path: Fill[], targetInput: BigNumber = POSITIVE_ } export function collapsePath(path: Fill[]): CollapsedFill[] { - const collapsed: Array = []; + const collapsed: CollapsedFill[] = []; for (const fill of path) { const source = fill.source; if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) { @@ -256,10 +252,10 @@ export function collapsePath(path: Fill[]): CollapsedFill[] { } collapsed.push({ source: fill.source, + fillData: fill.fillData, input: fill.input, output: fill.output, subFills: [fill], - nativeOrder: fill.source === ERC20BridgeSource.Native ? (fill.fillData as NativeFillData).order : undefined, }); } return collapsed; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts index 6f550a6310..08b5af74c8 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -20,6 +20,7 @@ import { AggregationError, DexSample, ERC20BridgeSource, + FeeSchedule, GetMarketOrdersOpts, OptimizedMarketOrder, OrderDomain, @@ -77,6 +78,7 @@ export class MarketOperationUtils { } const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]); + const sampleAmounts = getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase); // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( @@ -89,26 +91,32 @@ export class MarketOperationUtils { takerToken, ), // Get ETH -> maker token price. - DexOrderSampler.ops.getMedianSellRate( + await DexOrderSampler.ops.getMedianSellRateAsync( difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), makerToken, this._wethAddress, ONE_ETHER, this._wethAddress, + this._sampler.balancerPoolsCache, this._liquidityProviderRegistry, this._multiBridge, ), // Get sell quotes for taker -> maker. - DexOrderSampler.ops.getSellQuotes( - difference(SELL_SOURCES.concat(this._optionalSources()), _opts.excludedSources), + await DexOrderSampler.ops.getSellQuotesAsync( + difference( + SELL_SOURCES.concat(this._optionalSources()), + _opts.excludedSources.concat(ERC20BridgeSource.Balancer), + ), makerToken, takerToken, - getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase), + sampleAmounts, this._wethAddress, + this._sampler.balancerPoolsCache, this._liquidityProviderRegistry, this._multiBridge, ), ); + const rfqtPromise = getRfqtIndicativeQuotesAsync( nativeOrders[0].makerAssetData, nativeOrders[0].takerAssetData, @@ -116,14 +124,29 @@ export class MarketOperationUtils { takerAmount, _opts, ); + + const balancerPromise = this._sampler.executeAsync( + await DexOrderSampler.ops.getSellQuotesAsync( + difference([ERC20BridgeSource.Balancer], _opts.excludedSources), + makerToken, + takerToken, + sampleAmounts, + this._wethAddress, + this._sampler.balancerPoolsCache, + this._liquidityProviderRegistry, + this._multiBridge, + ), + ); + const [ [orderFillableAmounts, liquidityProviderAddress, ethToMakerAssetRate, dexQuotes], rfqtIndicativeQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise]); + [balancerQuotes], + ] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]); return this._generateOptimizedOrders({ orderFillableAmounts, nativeOrders, - dexQuotes, + dexQuotes: dexQuotes.concat(balancerQuotes), rfqtIndicativeQuotes, liquidityProviderAddress, multiBridgeAddress: this._multiBridge, @@ -159,6 +182,8 @@ export class MarketOperationUtils { } const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts }; const [makerToken, takerToken] = getNativeOrderTokens(nativeOrders[0]); + const sampleAmounts = getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase); + // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( // Get native order fillable amounts. @@ -170,30 +195,45 @@ export class MarketOperationUtils { takerToken, ), // Get ETH -> taker token price. - DexOrderSampler.ops.getMedianSellRate( + await DexOrderSampler.ops.getMedianSellRateAsync( difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), takerToken, this._wethAddress, ONE_ETHER, this._wethAddress, + this._sampler.balancerPoolsCache, this._liquidityProviderRegistry, this._multiBridge, ), // Get buy quotes for taker -> maker. - DexOrderSampler.ops.getBuyQuotes( + await DexOrderSampler.ops.getBuyQuotesAsync( difference( BUY_SOURCES.concat( this._liquidityProviderRegistry !== NULL_ADDRESS ? [ERC20BridgeSource.LiquidityProvider] : [], ), - _opts.excludedSources, + _opts.excludedSources.concat(ERC20BridgeSource.Balancer), ), makerToken, takerToken, - getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase), + sampleAmounts, this._wethAddress, + this._sampler.balancerPoolsCache, this._liquidityProviderRegistry, ), ); + + const balancerPromise = this._sampler.executeAsync( + await DexOrderSampler.ops.getBuyQuotesAsync( + difference([ERC20BridgeSource.Balancer], _opts.excludedSources), + makerToken, + takerToken, + sampleAmounts, + this._wethAddress, + this._sampler.balancerPoolsCache, + this._liquidityProviderRegistry, + ), + ); + const rfqtPromise = getRfqtIndicativeQuotesAsync( nativeOrders[0].makerAssetData, nativeOrders[0].takerAssetData, @@ -204,12 +244,13 @@ export class MarketOperationUtils { const [ [orderFillableAmounts, liquidityProviderAddress, ethToTakerAssetRate, dexQuotes], rfqtIndicativeQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise]); + [balancerQuotes], + ] = await Promise.all([samplerPromise, rfqtPromise, balancerPromise]); return this._generateOptimizedOrders({ orderFillableAmounts, nativeOrders, - dexQuotes, + dexQuotes: dexQuotes.concat(balancerQuotes), rfqtIndicativeQuotes, liquidityProviderAddress, multiBridgeAddress: this._multiBridge, @@ -251,24 +292,30 @@ export class MarketOperationUtils { const sources = difference(BUY_SOURCES, _opts.excludedSources); const ops = [ ...batchNativeOrders.map(orders => DexOrderSampler.ops.getOrderFillableMakerAmounts(orders)), - ...batchNativeOrders.map(orders => - DexOrderSampler.ops.getMedianSellRate( - difference(FEE_QUOTE_SOURCES, _opts.excludedSources), - getNativeOrderTokens(orders[0])[1], - this._wethAddress, - ONE_ETHER, - this._wethAddress, + ...(await Promise.all( + batchNativeOrders.map(async orders => + DexOrderSampler.ops.getMedianSellRateAsync( + difference(FEE_QUOTE_SOURCES, _opts.excludedSources), + getNativeOrderTokens(orders[0])[1], + this._wethAddress, + ONE_ETHER, + this._wethAddress, + this._sampler.balancerPoolsCache, + ), ), - ), - ...batchNativeOrders.map((orders, i) => - DexOrderSampler.ops.getBuyQuotes( - sources, - getNativeOrderTokens(orders[0])[0], - getNativeOrderTokens(orders[0])[1], - [makerAmounts[i]], - this._wethAddress, + )), + ...(await Promise.all( + batchNativeOrders.map(async (orders, i) => + DexOrderSampler.ops.getBuyQuotesAsync( + sources, + getNativeOrderTokens(orders[0])[0], + getNativeOrderTokens(orders[0])[1], + [makerAmounts[i]], + this._wethAddress, + this._sampler.balancerPoolsCache, + ), ), - ), + )), ]; const executeResults = await this._sampler.executeBatchAsync(ops); @@ -325,7 +372,7 @@ export class MarketOperationUtils { bridgeSlippage?: number; maxFallbackSlippage?: number; excludedSources?: ERC20BridgeSource[]; - feeSchedule?: { [source: string]: BigNumber }; + feeSchedule?: FeeSchedule; allowFallback?: boolean; shouldBatchBridgeOrders?: boolean; liquidityProviderAddress?: string; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/multibridge_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/multibridge_utils.ts index 8db19a8249..f5f698a839 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/multibridge_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/multibridge_utils.ts @@ -1,20 +1,20 @@ import { NULL_ADDRESS } from './constants'; // tslint:disable completed-docs - -export const TOKENS = { - WETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - DAI: '0x6b175474e89094c44da98b954eedeac495271d0f', - USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - MKR: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', -}; +// tslint:disable enum-naming +enum Tokens { + WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + DAI = '0x6b175474e89094c44da98b954eedeac495271d0f', + USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + MKR = '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', +} export function getMultiBridgeIntermediateToken(takerToken: string, makerToken: string): string { let intermediateToken = NULL_ADDRESS; - if (takerToken !== TOKENS.WETH && makerToken !== TOKENS.WETH) { - intermediateToken = TOKENS.WETH; - } else if (takerToken === TOKENS.USDC || makerToken === TOKENS.USDC) { - intermediateToken = TOKENS.DAI; + if (takerToken !== Tokens.WETH && makerToken !== Tokens.WETH) { + intermediateToken = Tokens.WETH; + } else if (takerToken === Tokens.USDC || makerToken === Tokens.USDC) { + intermediateToken = Tokens.DAI; } return intermediateToken; } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts index 19aa1b98fb..66ecf6387d 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -5,7 +5,6 @@ import { ERC20BridgeAssetData, SignedOrder } from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types'; -import { getCurveInfo } from '../source_utils'; import { ERC20_PROXY_ID, @@ -20,12 +19,15 @@ import { collapsePath } from './fills'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; import { AggregationError, + BalancerFillData, CollapsedFill, + CurveFillData, ERC20BridgeSource, Fill, NativeCollapsedFill, OptimizedMarketOrder, OrderDomain, + UniswapV2FillData, } from './types'; // tslint:disable completed-docs no-unnecessary-type-assertion @@ -151,7 +153,7 @@ export function createOrdersFromPath(path: Fill[], opts: CreateOrderFromPathOpts const orders: OptimizedMarketOrder[] = []; for (let i = 0; i < collapsedPath.length; ) { if (collapsedPath[i].source === ERC20BridgeSource.Native) { - orders.push(createNativeOrder(collapsedPath[i])); + orders.push(createNativeOrder(collapsedPath[i] as NativeCollapsedFill)); ++i; continue; } @@ -184,14 +186,11 @@ function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrder case ERC20BridgeSource.Uniswap: return opts.contractAddresses.uniswapBridge; case ERC20BridgeSource.UniswapV2: - case ERC20BridgeSource.UniswapV2Eth: return opts.contractAddresses.uniswapV2Bridge; - case ERC20BridgeSource.CurveUsdcDai: - case ERC20BridgeSource.CurveUsdcDaiUsdt: - case ERC20BridgeSource.CurveUsdcDaiUsdtTusd: - case ERC20BridgeSource.CurveUsdcDaiUsdtBusd: - case ERC20BridgeSource.CurveUsdcDaiUsdtSusd: + case ERC20BridgeSource.Curve: return opts.contractAddresses.curveBridge; + case ERC20BridgeSource.Balancer: + return opts.contractAddresses.balancerBridge; case ERC20BridgeSource.LiquidityProvider: if (opts.liquidityProviderAddress === undefined) { throw new Error('Cannot create a LiquidityProvider order without a LiquidityProvider pool address.'); @@ -214,39 +213,33 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): let makerAssetData; switch (fill.source) { - case ERC20BridgeSource.CurveUsdcDai: - case ERC20BridgeSource.CurveUsdcDaiUsdt: - case ERC20BridgeSource.CurveUsdcDaiUsdtTusd: - case ERC20BridgeSource.CurveUsdcDaiUsdtBusd: - case ERC20BridgeSource.CurveUsdcDaiUsdtSusd: - const { curveAddress, fromTokenIdx, toTokenIdx, version } = getCurveInfo( - fill.source, - takerToken, - makerToken, - ); + case ERC20BridgeSource.Curve: + const curveFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerToken, bridgeAddress, - createCurveBridgeData(curveAddress, fromTokenIdx, toTokenIdx, version), + createCurveBridgeData( + curveFillData.poolAddress, + curveFillData.fromTokenIdx, + curveFillData.toTokenIdx, + 1, // "version" + ), + ); + break; + case ERC20BridgeSource.Balancer: + const balancerFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createBalancerBridgeData(takerToken, balancerFillData.poolAddress), ); break; case ERC20BridgeSource.UniswapV2: + const uniswapV2FillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerToken, bridgeAddress, - createUniswapV2BridgeData([takerToken, makerToken]), - ); - break; - case ERC20BridgeSource.UniswapV2Eth: - if (opts.contractAddresses.etherToken === NULL_ADDRESS) { - throw new Error( - `Cannot create a ${ERC20BridgeSource.UniswapV2Eth.toString()} order without a WETH address`, - ); - } - makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( - makerToken, - bridgeAddress, - createUniswapV2BridgeData([takerToken, opts.contractAddresses.etherToken, makerToken]), + createUniswapV2BridgeData(uniswapV2FillData.tokenAddressPath), ); break; case ERC20BridgeSource.MultiBridge: @@ -338,6 +331,14 @@ function createMultiBridgeData(takerToken: string, makerToken: string): string { return encoder.encode({ takerToken, intermediateToken }); } +function createBalancerBridgeData(takerToken: string, poolAddress: string): string { + const encoder = AbiEncoder.create([ + { name: 'takerToken', type: 'address' }, + { name: 'poolAddress', type: 'address' }, + ]); + return encoder.encode({ takerToken, poolAddress }); +} + function createCurveBridgeData( curveAddress: string, fromTokenIdx: number, @@ -404,10 +405,10 @@ function createCommonBridgeOrderFields(opts: CreateOrderFromPathOpts): CommonBri }; } -function createNativeOrder(fill: CollapsedFill): OptimizedMarketOrder { +function createNativeOrder(fill: NativeCollapsedFill): OptimizedMarketOrder { return { fills: [fill], - ...(fill as NativeCollapsedFill).nativeOrder, + ...fill.fillData!.order, // tslint:disable-line:no-non-null-assertion }; } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts b/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts index effc284b59..3d5a20dca0 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts @@ -3,11 +3,13 @@ import { BigNumber } from '@0x/utils'; import { MarketOperation } from '../../types'; import { ZERO_AMOUNT } from './constants'; -import { getPathSize, isValidPath } from './fills'; +import { getPathAdjustedSize, getPathSize, isValidPath } from './fills'; import { Fill } from './types'; // tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs +const RUN_LIMIT_DECAY_FACTOR = 0.8; + /** * Find the optimal mixture of paths that maximizes (for sells) or minimizes * (for buys) output, while meeting the input requirement. @@ -16,11 +18,15 @@ export function findOptimalPath( side: MarketOperation, paths: Fill[][], targetInput: BigNumber, - runLimit?: number, + runLimit: number = 2 ** 15, ): Fill[] | undefined { - let optimalPath = paths[0] || []; - for (const path of paths.slice(1)) { - optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit); + // Sort paths in descending order by adjusted output amount. + const sortedPaths = paths + .slice(0) + .sort((a, b) => getPathAdjustedSize(b, targetInput)[1].comparedTo(getPathAdjustedSize(a, targetInput)[1])); + let optimalPath = sortedPaths[0] || []; + for (const [i, path] of sortedPaths.slice(1).entries()) { + optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit * RUN_LIMIT_DECAY_FACTOR ** i); } return isPathComplete(optimalPath, targetInput) ? optimalPath : undefined; } @@ -30,7 +36,7 @@ function mixPaths( pathA: Fill[], pathB: Fill[], targetInput: BigNumber, - maxSteps: number = 2 ** 15, + maxSteps: number, ): Fill[] { let bestPath: Fill[] = []; let bestPathInput = ZERO_AMOUNT; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts index 21df3ade96..30a79aff24 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -1,6 +1,7 @@ import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers'; import { BigNumber } from '@0x/utils'; +import { BalancerPoolsCache } from './balancer_utils'; import { samplerOperations } from './sampler_operations'; import { BatchedOperation } from './types'; @@ -11,6 +12,9 @@ export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, e const distribution = [...Array(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i)); const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution))); const amounts = stepSizes.map((_s, i) => { + if (i === numSamples - 1) { + return maxFillAmount; + } return maxFillAmount .times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)])) .integerValue(BigNumber.ROUND_UP); @@ -29,11 +33,11 @@ export class DexOrderSampler { * for use with `DexOrderSampler.executeAsync()`. */ public static ops = samplerOperations; - private readonly _samplerContract: IERC20BridgeSamplerContract; - constructor(samplerContract: IERC20BridgeSamplerContract) { - this._samplerContract = samplerContract; - } + constructor( + private readonly _samplerContract: IERC20BridgeSamplerContract, + public balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), + ) {} /* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */ diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index 6863c2b5b0..b1d9e47695 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -1,9 +1,20 @@ -import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..'; -import { getCurveInfo, isCurveSource } from '../source_utils'; +import * as _ from 'lodash'; -import { DEFAULT_FAKE_BUY_OPTS } from './constants'; +import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..'; + +import { BalancerPool, BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils'; +import { DEFAULT_FAKE_BUY_OPTS, MAINNET_CURVE_CONTRACTS } from './constants'; +import { getCurveAddressesForPair } from './curve_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; -import { BatchedOperation, DexSample, FakeBuyOpts } from './types'; +import { + BalancerFillData, + BatchedOperation, + CurveFillData, + DexSample, + FakeBuyOpts, + SourceQuoteOperation, + UniswapV2FillData, +} from './types'; /** * Composable operations that can be batched in a single transaction, @@ -34,12 +45,9 @@ export const samplerOperations = { }, }; }, - getKyberSellQuotes( - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): BatchedOperation { + getKyberSellQuotes(makerToken: string, takerToken: string, takerFillAmounts: BigNumber[]): SourceQuoteOperation { return { + source: ERC20BridgeSource.Kyber, encodeCall: contract => { return contract .sampleSellsFromKyberNetwork(takerToken, makerToken, takerFillAmounts) @@ -55,8 +63,9 @@ export const samplerOperations = { takerToken: string, makerFillAmounts: BigNumber[], fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.Kyber, encodeCall: contract => { return contract .sampleBuysFromKyberNetwork(takerToken, makerToken, makerFillAmounts, fakeBuyOpts) @@ -67,12 +76,9 @@ export const samplerOperations = { }, }; }, - getUniswapSellQuotes( - makerToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): BatchedOperation { + getUniswapSellQuotes(makerToken: string, takerToken: string, takerFillAmounts: BigNumber[]): SourceQuoteOperation { return { + source: ERC20BridgeSource.Uniswap, encodeCall: contract => { return contract .sampleSellsFromUniswap(takerToken, makerToken, takerFillAmounts) @@ -83,12 +89,9 @@ export const samplerOperations = { }, }; }, - getUniswapBuyQuotes( - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): BatchedOperation { + getUniswapBuyQuotes(makerToken: string, takerToken: string, makerFillAmounts: BigNumber[]): SourceQuoteOperation { return { + source: ERC20BridgeSource.Uniswap, encodeCall: contract => { return contract .sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts) @@ -99,8 +102,13 @@ export const samplerOperations = { }, }; }, - getUniswapV2SellQuotes(tokenAddressPath: string[], takerFillAmounts: BigNumber[]): BatchedOperation { + getUniswapV2SellQuotes( + tokenAddressPath: string[], + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.UniswapV2, + fillData: { tokenAddressPath }, encodeCall: contract => { return contract .sampleSellsFromUniswapV2(tokenAddressPath, takerFillAmounts) @@ -111,8 +119,13 @@ export const samplerOperations = { }, }; }, - getUniswapV2BuyQuotes(tokenAddressPath: string[], makerFillAmounts: BigNumber[]): BatchedOperation { + getUniswapV2BuyQuotes( + tokenAddressPath: string[], + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.UniswapV2, + fillData: { tokenAddressPath }, encodeCall: contract => { return contract .sampleBuysFromUniswapV2(tokenAddressPath, makerFillAmounts) @@ -128,8 +141,9 @@ export const samplerOperations = { makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.LiquidityProvider, encodeCall: contract => { return contract .sampleSellsFromLiquidityProviderRegistry(registryAddress, takerToken, makerToken, takerFillAmounts) @@ -143,41 +157,15 @@ export const samplerOperations = { }, }; }, - getMultiBridgeSellQuotes( - multiBridgeAddress: string, - makerToken: string, - intermediateToken: string, - takerToken: string, - takerFillAmounts: BigNumber[], - ): BatchedOperation { - return { - encodeCall: contract => { - return contract - .sampleSellsFromMultiBridge( - multiBridgeAddress, - takerToken, - intermediateToken, - makerToken, - takerFillAmounts, - ) - .getABIEncodedTransactionData(); - }, - handleCallResultsAsync: async (contract, callResults) => { - return contract.getABIDecodedReturnData( - 'sampleSellsFromLiquidityProviderRegistry', - callResults, - ); - }, - }; - }, getLiquidityProviderBuyQuotes( registryAddress: string, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.LiquidityProvider, encodeCall: contract => { return contract .sampleBuysFromLiquidityProviderRegistry( @@ -197,12 +185,34 @@ export const samplerOperations = { }, }; }, - getEth2DaiSellQuotes( + getMultiBridgeSellQuotes( + multiBridgeAddress: string, makerToken: string, + intermediateToken: string, takerToken: string, takerFillAmounts: BigNumber[], - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.MultiBridge, + encodeCall: contract => { + return contract + .sampleSellsFromMultiBridge( + multiBridgeAddress, + takerToken, + intermediateToken, + makerToken, + takerFillAmounts, + ) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleSellsFromMultiBridge', callResults); + }, + }; + }, + getEth2DaiSellQuotes(makerToken: string, takerToken: string, takerFillAmounts: BigNumber[]): SourceQuoteOperation { + return { + source: ERC20BridgeSource.Eth2Dai, encodeCall: contract => { return contract .sampleSellsFromEth2Dai(takerToken, makerToken, takerFillAmounts) @@ -213,12 +223,9 @@ export const samplerOperations = { }, }; }, - getEth2DaiBuyQuotes( - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): BatchedOperation { + getEth2DaiBuyQuotes(makerToken: string, takerToken: string, makerFillAmounts: BigNumber[]): SourceQuoteOperation { return { + source: ERC20BridgeSource.Eth2Dai, encodeCall: contract => { return contract .sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts) @@ -234,8 +241,14 @@ export const samplerOperations = { fromTokenIdx: number, toTokenIdx: number, takerFillAmounts: BigNumber[], - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.Curve, + fillData: { + poolAddress: curveAddress, + fromTokenIdx, + toTokenIdx, + }, encodeCall: contract => { return contract .sampleSellsFromCurve( @@ -256,8 +269,14 @@ export const samplerOperations = { fromTokenIdx: number, toTokenIdx: number, makerFillAmounts: BigNumber[], - ): BatchedOperation { + ): SourceQuoteOperation { return { + source: ERC20BridgeSource.Curve, + fillData: { + poolAddress: curveAddress, + fromTokenIdx, + toTokenIdx, + }, encodeCall: contract => { return contract .sampleBuysFromCurve( @@ -273,24 +292,40 @@ export const samplerOperations = { }, }; }, - getMedianSellRate( + getBalancerSellQuotes(pool: BalancerPool, takerFillAmounts: BigNumber[]): SourceQuoteOperation { + return { + source: ERC20BridgeSource.Balancer, + fillData: { poolAddress: pool.id }, + ...samplerOperations.constant(takerFillAmounts.map(amount => computeBalancerSellQuote(pool, amount))), + }; + }, + getBalancerBuyQuotes(pool: BalancerPool, makerFillAmounts: BigNumber[]): SourceQuoteOperation { + return { + source: ERC20BridgeSource.Balancer, + fillData: { poolAddress: pool.id }, + ...samplerOperations.constant(makerFillAmounts.map(amount => computeBalancerBuyQuote(pool, amount))), + }; + }, + getMedianSellRateAsync: async ( sources: ERC20BridgeSource[], makerToken: string, takerToken: string, takerFillAmount: BigNumber, wethAddress: string, + balancerPoolsCache?: BalancerPoolsCache, liquidityProviderRegistryAddress?: string, multiBridgeAddress?: string, - ): BatchedOperation { + ): Promise> => { if (makerToken.toLowerCase() === takerToken.toLowerCase()) { return samplerOperations.constant(new BigNumber(1)); } - const getSellQuotes = samplerOperations.getSellQuotes( + const getSellQuotes = await samplerOperations.getSellQuotesAsync( sources, makerToken, takerToken, [takerFillAmount], wethAddress, + balancerPoolsCache, liquidityProviderRegistryAddress, multiBridgeAddress, ); @@ -343,181 +378,221 @@ export const samplerOperations = { }, }; }, - getSellQuotes( + getSellQuotesAsync: async ( sources: ERC20BridgeSource[], makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], wethAddress: string, + balancerPoolsCache?: BalancerPoolsCache, liquidityProviderRegistryAddress?: string, multiBridgeAddress?: string, - ): BatchedOperation { - const subOps = sources - .map(source => { - let batchedOperation; - if (source === ERC20BridgeSource.Eth2Dai) { - batchedOperation = samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts); - } else if (source === ERC20BridgeSource.Uniswap) { - batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts); - } else if (source === ERC20BridgeSource.UniswapV2) { - batchedOperation = samplerOperations.getUniswapV2SellQuotes( - [takerToken, makerToken], - takerFillAmounts, - ); - } else if (source === ERC20BridgeSource.UniswapV2Eth) { - batchedOperation = samplerOperations.getUniswapV2SellQuotes( - [takerToken, wethAddress, makerToken], - takerFillAmounts, - ); - } else if (source === ERC20BridgeSource.Kyber) { - batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts); - } else if (isCurveSource(source)) { - const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken); - if (fromTokenIdx !== -1 && toTokenIdx !== -1) { - batchedOperation = samplerOperations.getCurveSellQuotes( - curveAddress, - fromTokenIdx, - toTokenIdx, - takerFillAmounts, - ); - } - } else if (source === ERC20BridgeSource.LiquidityProvider) { - if (liquidityProviderRegistryAddress === undefined) { - throw new Error( - 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', - ); - } - batchedOperation = samplerOperations.getLiquidityProviderSellQuotes( - liquidityProviderRegistryAddress, - makerToken, - takerToken, - takerFillAmounts, - ); - } else if (source === ERC20BridgeSource.MultiBridge) { - if (multiBridgeAddress === undefined) { - throw new Error('Cannot sample liquidity from MultiBridge if an address is not provided.'); - } - const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken); - batchedOperation = samplerOperations.getMultiBridgeSellQuotes( - multiBridgeAddress, - makerToken, - intermediateToken, - takerToken, - takerFillAmounts, - ); - } else { - throw new Error(`Unsupported sell sample source: ${source}`); - } - return { batchedOperation, source }; - }) - .filter(op => op.batchedOperation) as Array<{ - batchedOperation: BatchedOperation; - source: ERC20BridgeSource; - }>; + ): Promise> => { + const subOps = _.flatten( + await Promise.all( + sources.map( + async (source): Promise => { + switch (source) { + case ERC20BridgeSource.Eth2Dai: + return samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts); + case ERC20BridgeSource.Uniswap: + return samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts); + case ERC20BridgeSource.UniswapV2: + const ops = [ + samplerOperations.getUniswapV2SellQuotes( + [takerToken, makerToken], + takerFillAmounts, + ), + ]; + if (takerToken !== wethAddress && makerToken !== wethAddress) { + ops.push( + samplerOperations.getUniswapV2SellQuotes( + [takerToken, wethAddress, makerToken], + takerFillAmounts, + ), + ); + } + return ops; + case ERC20BridgeSource.Kyber: + return samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts); + case ERC20BridgeSource.Curve: + return getCurveAddressesForPair(takerToken, makerToken).map(curveAddress => + samplerOperations.getCurveSellQuotes( + curveAddress, + MAINNET_CURVE_CONTRACTS[curveAddress].indexOf(takerToken), + MAINNET_CURVE_CONTRACTS[curveAddress].indexOf(makerToken), + takerFillAmounts, + ), + ); + case ERC20BridgeSource.LiquidityProvider: + if (liquidityProviderRegistryAddress === undefined) { + throw new Error( + 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', + ); + } + return samplerOperations.getLiquidityProviderSellQuotes( + liquidityProviderRegistryAddress, + makerToken, + takerToken, + takerFillAmounts, + ); + case ERC20BridgeSource.MultiBridge: + if (multiBridgeAddress === undefined) { + throw new Error( + 'Cannot sample liquidity from MultiBridge if an address is not provided.', + ); + } + const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken); + return samplerOperations.getMultiBridgeSellQuotes( + multiBridgeAddress, + makerToken, + intermediateToken, + takerToken, + takerFillAmounts, + ); + // todo: refactor sampler ops to share state with DexOrderSampler so cache doesn't have to be passed as a param + case ERC20BridgeSource.Balancer: + if (balancerPoolsCache === undefined) { + throw new Error( + 'Cannot sample liquidity from Balancer if a cache is not provided.', + ); + } + const pools = await balancerPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + samplerOperations.getBalancerSellQuotes(pool, takerFillAmounts), + ); + default: + throw new Error(`Unsupported sell sample source: ${source}`); + } + }, + ), + ), + ); + const samplerOps = subOps.filter(op => op.source !== ERC20BridgeSource.Balancer); + const nonSamplerOps = subOps.filter(op => op.source === ERC20BridgeSource.Balancer); return { encodeCall: contract => { - const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract)); + const subCalls = samplerOps.map(op => op.encodeCall(contract)); return contract.batchCall(subCalls).getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { const rawSubCallResults = contract.getABIDecodedReturnData('batchCall', callResults); - const samples = await Promise.all( - subOps.map(async (op, i) => - op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]), - ), + let samples = await Promise.all( + samplerOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])), ); - return subOps.map((op, i) => { + samples = samples.concat( + await Promise.all(nonSamplerOps.map(async op => op.handleCallResultsAsync(contract, ''))), + ); + return [...samplerOps, ...nonSamplerOps].map((op, i) => { return samples[i].map((output, j) => ({ source: op.source, output, input: takerFillAmounts[j], + fillData: op.fillData, })); }); }, }; }, - getBuyQuotes( + getBuyQuotesAsync: async ( sources: ERC20BridgeSource[], makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], wethAddress: string, + balancerPoolsCache?: BalancerPoolsCache, liquidityProviderRegistryAddress?: string, fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, - ): BatchedOperation { - const subOps = sources - .map(source => { - let batchedOperation; - if (source === ERC20BridgeSource.Eth2Dai) { - batchedOperation = samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); - } else if (source === ERC20BridgeSource.Uniswap) { - batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); - } else if (source === ERC20BridgeSource.UniswapV2) { - batchedOperation = samplerOperations.getUniswapV2BuyQuotes( - [takerToken, makerToken], - makerFillAmounts, - ); - } else if (source === ERC20BridgeSource.UniswapV2Eth) { - batchedOperation = samplerOperations.getUniswapV2BuyQuotes( - [takerToken, wethAddress, makerToken], - makerFillAmounts, - ); - } else if (source === ERC20BridgeSource.Kyber) { - batchedOperation = samplerOperations.getKyberBuyQuotes( - makerToken, - takerToken, - makerFillAmounts, - fakeBuyOpts, - ); - } else if (isCurveSource(source)) { - const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken); - if (fromTokenIdx !== -1 && toTokenIdx !== -1) { - batchedOperation = samplerOperations.getCurveBuyQuotes( - curveAddress, - fromTokenIdx, - toTokenIdx, - makerFillAmounts, - ); - } - } else if (source === ERC20BridgeSource.LiquidityProvider) { - if (liquidityProviderRegistryAddress === undefined) { - throw new Error( - 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', - ); - } - batchedOperation = samplerOperations.getLiquidityProviderBuyQuotes( - liquidityProviderRegistryAddress, - makerToken, - takerToken, - makerFillAmounts, - fakeBuyOpts, - ); - } else { - throw new Error(`Unsupported buy sample source: ${source}`); - } - return { source, batchedOperation }; - }) - .filter(op => op.batchedOperation) as Array<{ - batchedOperation: BatchedOperation; - source: ERC20BridgeSource; - }>; + ): Promise> => { + const subOps = _.flatten( + await Promise.all( + sources.map( + async (source): Promise => { + switch (source) { + case ERC20BridgeSource.Eth2Dai: + return samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); + case ERC20BridgeSource.Uniswap: + return samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); + case ERC20BridgeSource.UniswapV2: + const ops = [ + samplerOperations.getUniswapV2BuyQuotes([takerToken, makerToken], makerFillAmounts), + ]; + if (takerToken !== wethAddress && makerToken !== wethAddress) { + ops.push( + samplerOperations.getUniswapV2BuyQuotes( + [takerToken, wethAddress, makerToken], + makerFillAmounts, + ), + ); + } + return ops; + case ERC20BridgeSource.Kyber: + return samplerOperations.getKyberBuyQuotes( + makerToken, + takerToken, + makerFillAmounts, + fakeBuyOpts, + ); + case ERC20BridgeSource.Curve: + return getCurveAddressesForPair(takerToken, makerToken).map(curveAddress => + samplerOperations.getCurveBuyQuotes( + curveAddress, + MAINNET_CURVE_CONTRACTS[curveAddress].indexOf(takerToken), + MAINNET_CURVE_CONTRACTS[curveAddress].indexOf(makerToken), + makerFillAmounts, + ), + ); + case ERC20BridgeSource.LiquidityProvider: + if (liquidityProviderRegistryAddress === undefined) { + throw new Error( + 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', + ); + } + return samplerOperations.getLiquidityProviderBuyQuotes( + liquidityProviderRegistryAddress, + makerToken, + takerToken, + makerFillAmounts, + fakeBuyOpts, + ); + case ERC20BridgeSource.Balancer: + if (balancerPoolsCache === undefined) { + throw new Error( + 'Cannot sample liquidity from Balancer if a cache is not provided.', + ); + } + const pools = await balancerPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + samplerOperations.getBalancerBuyQuotes(pool, makerFillAmounts), + ); + default: + throw new Error(`Unsupported buy sample source: ${source}`); + } + }, + ), + ), + ); + const samplerOps = subOps.filter(op => op.source !== ERC20BridgeSource.Balancer); + const nonSamplerOps = subOps.filter(op => op.source === ERC20BridgeSource.Balancer); return { encodeCall: contract => { - const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract)); + const subCalls = samplerOps.map(op => op.encodeCall(contract)); return contract.batchCall(subCalls).getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { const rawSubCallResults = contract.getABIDecodedReturnData('batchCall', callResults); - const samples = await Promise.all( - subOps.map(async (op, i) => - op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]), - ), + let samples = await Promise.all( + samplerOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])), ); - return subOps.map((op, i) => { + samples = samples.concat( + await Promise.all(nonSamplerOps.map(async op => op.handleCallResultsAsync(contract, ''))), + ); + return [...samplerOps, ...nonSamplerOps].map((op, i) => { return samples[i].map((output, j) => ({ source: op.source, output, input: makerFillAmounts[j], + fillData: op.fillData, })); }); }, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index c746f469a6..36a2b2f8c9 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -29,16 +29,12 @@ export enum ERC20BridgeSource { Native = 'Native', Uniswap = 'Uniswap', UniswapV2 = 'Uniswap_V2', - UniswapV2Eth = 'Uniswap_V2_ETH', Eth2Dai = 'Eth2Dai', Kyber = 'Kyber', - CurveUsdcDai = 'Curve_USDC_DAI', - CurveUsdcDaiUsdt = 'Curve_USDC_DAI_USDT', - CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD', - CurveUsdcDaiUsdtBusd = 'Curve_USDC_DAI_USDT_BUSD', - CurveUsdcDaiUsdtSusd = 'Curve_USDC_DAI_USDT_SUSD', + Curve = 'Curve', LiquidityProvider = 'LiquidityProvider', MultiBridge = 'MultiBridge', + Balancer = 'Balancer', } // Internal `fillData` field for `Fill` objects. @@ -49,13 +45,28 @@ export interface NativeFillData extends FillData { order: SignedOrderWithFillableAmounts; } +export interface CurveFillData extends FillData { + poolAddress: string; + fromTokenIdx: number; + toTokenIdx: number; +} + +export interface BalancerFillData extends FillData { + poolAddress: string; +} + +export interface UniswapV2FillData extends FillData { + tokenAddressPath: string[]; +} + /** * Represents an individual DEX sample from the sampler contract. */ -export interface DexSample { +export interface DexSample { source: ERC20BridgeSource; input: BigNumber; output: BigNumber; + fillData?: TFillData; } /** @@ -71,7 +82,7 @@ export enum FillFlags { /** * Represents a node on a fill path. */ -export interface Fill { +export interface Fill { // See `FillFlags`. flags: FillFlags; // Input fill amount (taker asset amount in a sell, maker asset amount in a buy). @@ -92,13 +103,13 @@ export interface Fill { source: ERC20BridgeSource; // Data associated with this this Fill object. Used to reconstruct orders // from paths. - fillData?: FillData | NativeFillData; + fillData?: TFillData; } /** * Represents continguous fills on a path that have been merged together. */ -export interface CollapsedFill { +export interface CollapsedFill { /** * The source DEX. */ @@ -118,14 +129,14 @@ export interface CollapsedFill { input: BigNumber; output: BigNumber; }>; + + fillData?: TFillData; } /** * A `CollapsedFill` wrapping a native order. */ -export interface NativeCollapsedFill extends CollapsedFill { - nativeOrder: SignedOrderWithFillableAmounts; -} +export interface NativeCollapsedFill extends CollapsedFill {} /** * Optimized orders to fill. @@ -141,6 +152,9 @@ export interface GetMarketOrdersRfqtOpts extends RfqtRequestOpts { quoteRequestor?: QuoteRequestor; } +export type FeeEstimate = (fillData?: FillData) => number | BigNumber; +export type FeeSchedule = Partial<{ [key in ERC20BridgeSource]: FeeEstimate }>; + /** * Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`. */ @@ -184,11 +198,11 @@ export interface GetMarketOrdersOpts { /** * Fees for each liquidity source, expressed in gas. */ - feeSchedule: { [source: string]: BigNumber }; + feeSchedule: FeeSchedule; /** * Estimated gas consumed by each liquidity source. */ - gasSchedule: { [source: string]: number }; + gasSchedule: FeeSchedule; /** * Whether to pad the quote with a redundant fallback quote using different * sources. Defaults to `true`. @@ -210,6 +224,11 @@ export interface BatchedOperation { handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise; } +export interface SourceQuoteOperation extends BatchedOperation { + source: ERC20BridgeSource; + fillData?: TFillData; +} + /** * Used in the ERC20BridgeSampler when a source does not natively * support sampling via a specific buy amount. diff --git a/packages/asset-swapper/src/utils/quote_simulation.ts b/packages/asset-swapper/src/utils/quote_simulation.ts index a18eb92775..6c22008fd5 100644 --- a/packages/asset-swapper/src/utils/quote_simulation.ts +++ b/packages/asset-swapper/src/utils/quote_simulation.ts @@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils'; import { constants } from '../constants'; import { MarketOperation } from '../types'; -import { CollapsedFill, ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types'; +import { CollapsedFill, FeeSchedule, OptimizedMarketOrder } from './market_operation_utils/types'; import { isOrderTakerFeePayableWithMakerAsset, isOrderTakerFeePayableWithTakerAsset } from './utils'; const { PROTOCOL_FEE_MULTIPLIER, ZERO_AMOUNT } = constants; @@ -71,7 +71,7 @@ export interface QuoteFillInfo { } export interface QuoteFillInfoOpts { - gasSchedule: { [soruce: string]: number }; + gasSchedule: FeeSchedule; protocolFeeMultiplier: BigNumber; } @@ -124,10 +124,7 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult opts.gasSchedule, ), // Worst case gas and protocol fee is hitting all orders. - gas: getTotalGasUsedBySources( - getFlattenedFillsFromOrders(quoteInfo.orders).map(s => s.source), - opts.gasSchedule, - ), + gas: getTotalGasUsedByFills(getFlattenedFillsFromOrders(quoteInfo.orders), opts.gasSchedule), protocolFee: protocolFeePerFillOrder.times(quoteInfo.orders.length), }; return fromIntermediateQuoteFillResult(result, quoteInfo); @@ -137,7 +134,7 @@ export function fillQuoteOrders( fillOrders: QuoteFillOrderCall[], inputAmount: BigNumber, protocolFeePerFillOrder: BigNumber, - gasSchedule: { [source: string]: number }, + gasSchedule: FeeSchedule, ): IntermediateQuoteFillResult { const result: IntermediateQuoteFillResult = { ...EMPTY_QUOTE_INTERMEDIATE_FILL_RESULT, @@ -152,8 +149,9 @@ export function fillQuoteOrders( if (remainingInput.lte(0)) { break; } - const { source } = fill; - result.gas += gasSchedule[source] || 0; + const { source, fillData } = fill; + const fee = gasSchedule[source] === undefined ? 0 : gasSchedule[source]!(fillData); + result.gas += new BigNumber(fee).toNumber(); result.inputBySource[source] = result.inputBySource[source] || ZERO_AMOUNT; // Actual rates are rarely linear, so fill subfills individually to @@ -347,10 +345,11 @@ export function getFlattenedFillsFromOrders(orders: OptimizedMarketOrder[]): Col return fills; } -function getTotalGasUsedBySources(sources: ERC20BridgeSource[], gasSchedule: { [source: string]: number }): number { +function getTotalGasUsedByFills(fills: CollapsedFill[], gasSchedule: FeeSchedule): number { let gasUsed = 0; - for (const s of sources) { - gasUsed += gasSchedule[s] || 0; + for (const f of fills) { + const fee = gasSchedule[f.source] === undefined ? 0 : gasSchedule[f.source]!(f.fillData); + gasUsed += new BigNumber(fee).toNumber(); } return gasUsed; } diff --git a/packages/asset-swapper/src/utils/source_utils.ts b/packages/asset-swapper/src/utils/source_utils.ts deleted file mode 100644 index d4aa0412f5..0000000000 --- a/packages/asset-swapper/src/utils/source_utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { DEFAULT_CURVE_OPTS } from './market_operation_utils/constants'; -import { ERC20BridgeSource } from './market_operation_utils/types'; - -export const isCurveSource = (source: ERC20BridgeSource): boolean => { - return Object.keys(DEFAULT_CURVE_OPTS).includes(source); -}; - -export const getCurveInfo = ( - source: ERC20BridgeSource, - takerToken: string, - makerToken: string, -): { curveAddress: string; fromTokenIdx: number; toTokenIdx: number; version: number } => { - const { curveAddress, tokens, version } = DEFAULT_CURVE_OPTS[source]; - const fromTokenIdx = tokens.indexOf(takerToken); - const toTokenIdx = tokens.indexOf(makerToken); - return { curveAddress, fromTokenIdx, toTokenIdx, version }; -}; diff --git a/packages/asset-swapper/src/utils/swap_quote_calculator.ts b/packages/asset-swapper/src/utils/swap_quote_calculator.ts index ffa5d2aaa7..77ebff52b2 100644 --- a/packages/asset-swapper/src/utils/swap_quote_calculator.ts +++ b/packages/asset-swapper/src/utils/swap_quote_calculator.ts @@ -17,7 +17,7 @@ import { import { MarketOperationUtils } from './market_operation_utils'; import { convertNativeOrderToFullyFillableOptimizedOrders } from './market_operation_utils/orders'; -import { GetMarketOrdersOpts, OptimizedMarketOrder } from './market_operation_utils/types'; +import { FeeSchedule, FillData, GetMarketOrdersOpts, OptimizedMarketOrder } from './market_operation_utils/types'; import { isSupportedAssetDataInOrders } from './utils'; import { QuoteFillResult, simulateBestCaseFill, simulateWorstCaseFill } from './quote_simulation'; @@ -126,7 +126,9 @@ export class SwapQuoteCalculator { // Scale fees by gas price. const _opts: GetMarketOrdersOpts = { ...opts, - feeSchedule: _.mapValues(opts.feeSchedule, v => v.times(gasPrice)), + feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData?: FillData) => + gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)), + ), }; const firstOrderMakerAssetData = !!prunedOrders[0] @@ -174,7 +176,7 @@ function createSwapQuote( operation: MarketOperation, assetFillAmount: BigNumber, gasPrice: BigNumber, - gasSchedule: { [source: string]: number }, + gasSchedule: FeeSchedule, ): SwapQuote { const bestCaseFillResult = simulateBestCaseFill({ gasPrice, diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts index ef42cffaec..310d2c1a46 100644 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ b/packages/asset-swapper/test/dex_sampler_test.ts @@ -12,9 +12,15 @@ import { SignedOrder } from '@0x/types'; import { BigNumber, hexUtils } from '@0x/utils'; import * as _ from 'lodash'; +import { + BalancerPool, + computeBalancerBuyQuote, + computeBalancerSellQuote, +} from '../src/utils/market_operation_utils/balancer_utils'; import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler'; -import { ERC20BridgeSource } from '../src/utils/market_operation_utils/types'; +import { ERC20BridgeSource, FillData } from '../src/utils/market_operation_utils/types'; +import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache'; import { MockSamplerContract } from './utils/mock_sampler_contract'; const CHAIN_ID = 1; @@ -149,7 +155,7 @@ describe('DexSampler tests', () => { const expectedTakerToken = randomAddress(); const registry = randomAddress(); const sampler = new MockSamplerContract({ - sampleSellsFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, fillAmounts) => { + sampleSellsFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => { expect(registryAddress).to.eq(registry); expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); @@ -158,12 +164,13 @@ describe('DexSampler tests', () => { }); const dexOrderSampler = new DexOrderSampler(sampler); const [result] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getSellQuotes( + await DexOrderSampler.ops.getSellQuotesAsync( [ERC20BridgeSource.LiquidityProvider], expectedMakerToken, expectedTakerToken, [toBaseUnitAmount(1000)], wethAddress, + dexOrderSampler.balancerPoolsCache, registry, ), ); @@ -173,6 +180,7 @@ describe('DexSampler tests', () => { source: 'LiquidityProvider', output: toBaseUnitAmount(1001), input: toBaseUnitAmount(1000), + fillData: undefined, }, ], ]); @@ -183,7 +191,7 @@ describe('DexSampler tests', () => { const expectedTakerToken = randomAddress(); const registry = randomAddress(); const sampler = new MockSamplerContract({ - sampleBuysFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, fillAmounts) => { + sampleBuysFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => { expect(registryAddress).to.eq(registry); expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); @@ -192,12 +200,13 @@ describe('DexSampler tests', () => { }); const dexOrderSampler = new DexOrderSampler(sampler); const [result] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getBuyQuotes( + await DexOrderSampler.ops.getBuyQuotesAsync( [ERC20BridgeSource.LiquidityProvider], expectedMakerToken, expectedTakerToken, [toBaseUnitAmount(1000)], wethAddress, + dexOrderSampler.balancerPoolsCache, registry, ), ); @@ -207,6 +216,7 @@ describe('DexSampler tests', () => { source: 'LiquidityProvider', output: toBaseUnitAmount(999), input: toBaseUnitAmount(1000), + fillData: undefined, }, ], ]); @@ -233,12 +243,13 @@ describe('DexSampler tests', () => { }); const dexOrderSampler = new DexOrderSampler(sampler); const [result] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getSellQuotes( + await DexOrderSampler.ops.getSellQuotesAsync( [ERC20BridgeSource.MultiBridge], expectedMakerToken, expectedTakerToken, [toBaseUnitAmount(1000)], randomAddress(), + dexOrderSampler.balancerPoolsCache, randomAddress(), multiBridge, ), @@ -249,6 +260,7 @@ describe('DexSampler tests', () => { source: 'MultiBridge', output: toBaseUnitAmount(1001), input: toBaseUnitAmount(1000), + fillData: undefined, }, ], ]); @@ -412,72 +424,92 @@ describe('DexSampler tests', () => { return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); }, sampleSellsFromUniswapV2: (path, fillAmounts) => { - expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + if (path.length === 2) { + expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + } else if (path.length === 3) { + expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); + } else { + expect(path).to.have.lengthOf.within(2, 3); + } expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); }, }); const dexOrderSampler = new DexOrderSampler(sampler); const [quotes] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getSellQuotes( + await DexOrderSampler.ops.getSellQuotesAsync( sources, expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts, wethAddress, + dexOrderSampler.balancerPoolsCache, ), ); - expect(quotes).to.be.length(sources.length); const expectedQuotes = sources.map(s => expectedTakerFillAmounts.map(a => ({ source: s, input: a, output: a.times(ratesBySource[s]).integerValue(), + fillData: + s === ERC20BridgeSource.UniswapV2 + ? { tokenAddressPath: [expectedTakerToken, expectedMakerToken] } + : ((undefined as any) as FillData), })), ); - expect(quotes).to.deep.eq(expectedQuotes); + const uniswapV2ETHQuotes = [ + expectedTakerFillAmounts.map(a => ({ + source: ERC20BridgeSource.UniswapV2, + input: a, + output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), + fillData: { + tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], + }, + })), + ]; + // extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB) + expect(quotes).to.have.lengthOf(sources.length + 1); + expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes)); }); - - it('getSellQuotes() includes ETH for Uniswap_V2_ETH', async () => { + it('getSellQuotes() uses samples from Balancer', async () => { const expectedTakerToken = randomAddress(); const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.UniswapV2Eth]; - const ratesBySource: RatesBySource = { - [ERC20BridgeSource.UniswapV2Eth]: getRandomFloat(0, 100), - }; const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); - const sampler = new MockSamplerContract({ - sampleSellsFromUniswapV2: (path, fillAmounts) => { - expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); - expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2Eth]).integerValue()); + const pools: BalancerPool[] = [generateBalancerPool(), generateBalancerPool()]; + const balancerPoolsCache = new MockBalancerPoolsCache({ + getPoolsForPairAsync: async (takerToken: string, makerToken: string) => { + expect(takerToken).equal(expectedTakerToken); + expect(makerToken).equal(expectedMakerToken); + return Promise.resolve(pools); }, }); - const dexOrderSampler = new DexOrderSampler(sampler); + const dexOrderSampler = new DexOrderSampler(new MockSamplerContract({}), balancerPoolsCache); const [quotes] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getSellQuotes( - sources, + await DexOrderSampler.ops.getSellQuotesAsync( + [ERC20BridgeSource.Balancer], expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts, wethAddress, + dexOrderSampler.balancerPoolsCache, ), ); - expect(quotes).to.be.length(sources.length); - const expectedQuotes = sources.map(s => + const expectedQuotes = pools.map(p => expectedTakerFillAmounts.map(a => ({ - source: s, + source: ERC20BridgeSource.Balancer, input: a, - output: a.times(ratesBySource[s]).integerValue(), + output: computeBalancerSellQuote(p, a), + fillData: { poolAddress: p.id }, })), ); + expect(quotes).to.have.lengthOf(2); // one array per pool expect(quotes).to.deep.eq(expectedQuotes); }); it('getBuyQuotes()', async () => { const expectedTakerToken = randomAddress(); const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap]; + const sources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2]; const ratesBySource: RatesBySource = { [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), @@ -498,78 +530,85 @@ describe('DexSampler tests', () => { return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); }, sampleBuysFromUniswapV2: (path, fillAmounts) => { - expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + if (path.length === 2) { + expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + } else if (path.length === 3) { + expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); + } else { + expect(path).to.have.lengthOf.within(2, 3); + } expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); }, }); const dexOrderSampler = new DexOrderSampler(sampler); const [quotes] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getBuyQuotes( + await DexOrderSampler.ops.getBuyQuotesAsync( sources, expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts, wethAddress, + dexOrderSampler.balancerPoolsCache, ), ); - expect(quotes).to.be.length(sources.length); const expectedQuotes = sources.map(s => expectedMakerFillAmounts.map(a => ({ source: s, input: a, output: a.times(ratesBySource[s]).integerValue(), + fillData: + s === ERC20BridgeSource.UniswapV2 + ? { tokenAddressPath: [expectedTakerToken, expectedMakerToken] } + : ((undefined as any) as FillData), })), ); - expect(quotes).to.deep.eq(expectedQuotes); + const uniswapV2ETHQuotes = [ + expectedMakerFillAmounts.map(a => ({ + source: ERC20BridgeSource.UniswapV2, + input: a, + output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), + fillData: { + tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], + }, + })), + ]; + // extra quote for Uniswap V2, which provides a direct quote (tokenA -> tokenB) AND an ETH quote (tokenA -> ETH -> tokenB) + expect(quotes).to.have.lengthOf(sources.length + 1); + expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes)); }); - it('getBuyQuotes() includes ETH for Uniswap_V2_ETH', async () => { + it('getBuyQuotes() uses samples from Balancer', async () => { const expectedTakerToken = randomAddress(); const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2Eth]; - const ratesBySource: RatesBySource = { - [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), - [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), - [ERC20BridgeSource.UniswapV2Eth]: getRandomFloat(0, 100), - }; const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); - const sampler = new MockSamplerContract({ - sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => { - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()); - }, - sampleBuysFromEth2Dai: (takerToken, makerToken, fillAmounts) => { - expect(takerToken).to.eq(expectedTakerToken); - expect(makerToken).to.eq(expectedMakerToken); - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); - }, - sampleBuysFromUniswapV2: (path, fillAmounts) => { - expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); - expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); - return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2Eth]).integerValue()); + const pools: BalancerPool[] = [generateBalancerPool(), generateBalancerPool()]; + const balancerPoolsCache = new MockBalancerPoolsCache({ + getPoolsForPairAsync: async (takerToken: string, makerToken: string) => { + expect(takerToken).equal(expectedTakerToken); + expect(makerToken).equal(expectedMakerToken); + return Promise.resolve(pools); }, }); - const dexOrderSampler = new DexOrderSampler(sampler); + const dexOrderSampler = new DexOrderSampler(new MockSamplerContract({}), balancerPoolsCache); const [quotes] = await dexOrderSampler.executeAsync( - DexOrderSampler.ops.getBuyQuotes( - sources, + await DexOrderSampler.ops.getBuyQuotesAsync( + [ERC20BridgeSource.Balancer], expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts, wethAddress, + dexOrderSampler.balancerPoolsCache, ), ); - expect(quotes).to.be.length(sources.length); - const expectedQuotes = sources.map(s => + const expectedQuotes = pools.map(p => expectedMakerFillAmounts.map(a => ({ - source: s, + source: ERC20BridgeSource.Balancer, input: a, - output: a.times(ratesBySource[s]).integerValue(), + output: computeBalancerBuyQuote(p, a), + fillData: { poolAddress: p.id }, })), ); + expect(quotes).to.have.lengthOf(2); // one set per pool expect(quotes).to.deep.eq(expectedQuotes); }); }); @@ -600,4 +639,14 @@ describe('DexSampler tests', () => { }); }); }); +function generateBalancerPool(): BalancerPool { + return { + id: randomAddress(), + balanceIn: getRandomInteger(1, 1e18), + balanceOut: getRandomInteger(1, 1e18), + weightIn: getRandomInteger(0, 1e5), + weightOut: getRandomInteger(0, 1e5), + swapFee: getRandomInteger(0, 1e5), + }; +} // tslint:disable-next-line: max-file-line-count diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index d0a141d7ba..506df0e6e5 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -16,16 +16,10 @@ import * as _ from 'lodash'; import { MarketOperation, SignedOrderWithFillableAmounts } from '../src'; import { MarketOperationUtils } from '../src/utils/market_operation_utils/'; -import { - BUY_SOURCES, - DEFAULT_CURVE_OPTS, - POSITIVE_INF, - SELL_SOURCES, - ZERO_AMOUNT, -} from '../src/utils/market_operation_utils/constants'; +import { BUY_SOURCES, POSITIVE_INF, SELL_SOURCES, ZERO_AMOUNT } from '../src/utils/market_operation_utils/constants'; import { createFillPaths } from '../src/utils/market_operation_utils/fills'; import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; -import { DexSample, ERC20BridgeSource, NativeFillData } from '../src/utils/market_operation_utils/types'; +import { DexSample, ERC20BridgeSource, FillData, NativeFillData } from '../src/utils/market_operation_utils/types'; // tslint:disable: custom-no-magic-numbers describe('MarketOperationUtils tests', () => { @@ -93,10 +87,7 @@ describe('MarketOperationUtils tests', () => { case UNISWAP_V2_BRIDGE_ADDRESS.toLowerCase(): return ERC20BridgeSource.UniswapV2; case CURVE_BRIDGE_ADDRESS.toLowerCase(): - const curveSource = Object.keys(DEFAULT_CURVE_OPTS).filter( - k => assetData.indexOf(DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1, - ); - return curveSource[0] as ERC20BridgeSource; + return ERC20BridgeSource.Curve; default: break; } @@ -132,12 +123,18 @@ describe('MarketOperationUtils tests', () => { chainId: CHAIN_ID, }; - function createSamplesFromRates(source: ERC20BridgeSource, inputs: Numberish[], rates: Numberish[]): DexSample[] { + function createSamplesFromRates( + source: ERC20BridgeSource, + inputs: Numberish[], + rates: Numberish[], + fillData?: FillData, + ): DexSample[] { const samples: DexSample[] = []; inputs.forEach((input, i) => { const rate = rates[i]; samples.push({ source, + fillData: fillData || DEFAULT_FILL_DATA[source], input: new BigNumber(input), output: new BigNumber(input) .minus(i === 0 ? 0 : samples[i - 1].input) @@ -161,10 +158,10 @@ describe('MarketOperationUtils tests', () => { function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { return ( sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, + _makerToken: string, + _takerToken: string, fillAmounts: BigNumber[], - wethAddress: string, + _wethAddress: string, ) => { return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s])); }; @@ -184,10 +181,11 @@ describe('MarketOperationUtils tests', () => { takerToken: string, fillAmounts: BigNumber[], wethAddress: string, + _balancerPoolsCache?: any, liquidityProviderAddress?: string, ) => { liquidityPoolParams.liquidityProviderAddress = liquidityProviderAddress; - liquidityPoolParams.sources = sources; + liquidityPoolParams.sources = liquidityPoolParams.sources.concat(sources); return tradeOperation(rates)( sources, makerToken, @@ -203,10 +201,10 @@ describe('MarketOperationUtils tests', () => { function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { return ( sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, + _makerToken: string, + _takerToken: string, fillAmounts: BigNumber[], - wethAddress: string, + _wethAddress: string, ) => { return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s].map(r => new BigNumber(1).div(r)))); }; @@ -229,18 +227,18 @@ describe('MarketOperationUtils tests', () => { function createGetMedianSellRate(rate: Numberish): GetMedianRateOperation { return ( - sources: ERC20BridgeSource[], - makerToken: string, - takerToken: string, - fillAmounts: BigNumber[], - wethAddress: string, + _sources: ERC20BridgeSource[], + _makerToken: string, + _takerToken: string, + _fillAmounts: BigNumber[], + _wethAddress: string, ) => { return new BigNumber(rate); }; } function getLiquidityProviderFromRegistry(): GetLiquidityProviderFromRegistryOperation { - return (registryAddress: string, takerToken: string, makerToken: string): string => { + return (_registryAddress: string, _takerToken: string, _makerToken: string): string => { return NULL_ADDRESS; }; } @@ -288,17 +286,23 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES), [ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES), [ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.UniswapV2]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.UniswapV2Eth]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.CurveUsdcDai]: _.times(NUM_SAMPLES, () => 0), - [ERC20BridgeSource.CurveUsdcDaiUsdt]: _.times(NUM_SAMPLES, () => 0), - [ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: _.times(NUM_SAMPLES, () => 0), - [ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: _.times(NUM_SAMPLES, () => 0), - [ERC20BridgeSource.CurveUsdcDaiUsdtSusd]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.UniswapV2]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Balancer]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Curve]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0), }; + interface FillDataBySource { + [source: string]: FillData; + } + + const DEFAULT_FILL_DATA: FillDataBySource = { + [ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] }, + [ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() }, + [ERC20BridgeSource.Curve]: { poolAddress: randomAddress(), fromTokenIdx: 0, toTokenIdx: 1 }, + }; + const DEFAULT_OPS = { getOrderFillableTakerAmounts(orders: SignedOrder[]): BigNumber[] { return orders.map(o => o.takerAssetAmount); @@ -306,9 +310,9 @@ describe('MarketOperationUtils tests', () => { getOrderFillableMakerAmounts(orders: SignedOrder[]): BigNumber[] { return orders.map(o => o.makerAssetAmount); }, - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES), - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES), - getMedianSellRate: createGetMedianSellRate(1), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES), + getMedianSellRateAsync: createGetMedianSellRate(1), getLiquidityProviderFromRegistry: getLiquidityProviderFromRegistry(), }; @@ -346,11 +350,7 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: [ - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2Eth, - ...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), - ], + excludedSources: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Curve, ERC20BridgeSource.Balancer], allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -363,9 +363,9 @@ describe('MarketOperationUtils tests', () => { const numSamples = _.random(1, NUM_SAMPLES); let actualNumSamples = 0; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { + getSellQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { actualNumSamples = amounts.length; - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); + return DEFAULT_OPS.getSellQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -378,9 +378,9 @@ describe('MarketOperationUtils tests', () => { it('polls all DEXes if `excludedSources` is empty', async () => { let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sources.slice(); - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); + getSellQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { + sourcesPolled = sourcesPolled.concat(sources.slice()); + return DEFAULT_OPS.getSellQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -396,7 +396,7 @@ describe('MarketOperationUtils tests', () => { DEFAULT_RATES, ); replaceSamplerOps({ - getSellQuotes: fn, + getSellQuotesAsync: fn, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -419,9 +419,9 @@ describe('MarketOperationUtils tests', () => { const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length)); let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sources.slice(); - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); + getSellQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { + sourcesPolled = sourcesPolled.concat(sources.slice()); + return DEFAULT_OPS.getSellQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -477,7 +477,7 @@ describe('MarketOperationUtils tests', () => { expect(improvedOrders).to.not.be.length(0); for (const order of improvedOrders) { const expectedMakerAmount = order.fills[0].output; - const slippage = 1 - order.makerAssetAmount.div(expectedMakerAmount.plus(1)).toNumber(); + const slippage = new BigNumber(1).minus(order.makerAssetAmount.div(expectedMakerAmount.plus(1))); assertRoughlyEquals(slippage, bridgeSlippage, 1); } }); @@ -485,11 +485,11 @@ describe('MarketOperationUtils tests', () => { it('can mix convex sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.UniswapV2] = [0.5, 0.05, 0.05, 0.05]; + rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; // unused replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -499,7 +499,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ERC20BridgeSource.Native, ERC20BridgeSource.Native, ]; @@ -514,18 +514,20 @@ describe('MarketOperationUtils tests', () => { const nativeFeeRate = 0.06; const rates: RatesBySource = { [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, 0.93, 0.92, 0.91] - [ERC20BridgeSource.UniswapV2]: [0.96, 0.1, 0.1, 0.1], + [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], }; const feeSchedule = { - [ERC20BridgeSource.Native]: FILL_AMOUNT.div(4) - .times(nativeFeeRate) - .dividedToIntegerBy(ETH_TO_MAKER_RATE), + [ERC20BridgeSource.Native]: _.constant( + FILL_AMOUNT.div(4) + .times(nativeFeeRate) + .dividedToIntegerBy(ETH_TO_MAKER_RATE), + ), }; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), + getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -535,7 +537,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Native, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Native, ]; @@ -551,16 +553,18 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.UniswapV2]: [1, 0.7, 0.2, 0.2], + [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], }; const feeSchedule = { - [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) - .times(uniswapFeeRate) - .dividedToIntegerBy(ETH_TO_MAKER_RATE), + [ERC20BridgeSource.Uniswap]: _.constant( + FILL_AMOUNT.div(4) + .times(uniswapFeeRate) + .dividedToIntegerBy(ETH_TO_MAKER_RATE), + ), }; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), + getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -571,7 +575,7 @@ describe('MarketOperationUtils tests', () => { const expectedSources = [ ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); }); @@ -580,12 +584,12 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = { [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], // Won't use [ERC20BridgeSource.Eth2Dai]: [0.5, 0.85, 0.75, 0.75], // Concave - [ERC20BridgeSource.UniswapV2]: [0.96, 0.2, 0.1, 0.1], + [ERC20BridgeSource.Uniswap]: [0.96, 0.2, 0.1, 0.1], [ERC20BridgeSource.Native]: [0.95, 0.2, 0.2, 0.1], }; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), - getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), + getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_MAKER_RATE), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -595,7 +599,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ERC20BridgeSource.Native, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); @@ -604,11 +608,11 @@ describe('MarketOperationUtils tests', () => { it('fallback orders use different sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; - rates[ERC20BridgeSource.UniswapV2] = [0.6, 0.05, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01]; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -620,7 +624,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Native, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ]; const secondSources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber]; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); @@ -630,11 +634,11 @@ describe('MarketOperationUtils tests', () => { it('does not create a fallback if below maxFallbackSlippage', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.UniswapV2] = [1, 1, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01]; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -642,7 +646,7 @@ describe('MarketOperationUtils tests', () => { { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, ); const orderSources = improvedOrders.map(o => o.fills[0].source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV2]; + const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; const secondSources: ERC20BridgeSource[] = []; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); @@ -667,7 +671,7 @@ describe('MarketOperationUtils tests', () => { ] = getLiquidityProviderFromRegistryAndReturnCallParameters(liquidityProviderAddress); replaceSamplerOps({ getOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT], - getSellQuotes: getSellQuotesFn, + getSellQuotesAsync: getSellQuotesFn, getLiquidityProviderFromRegistry: getLiquidityProviderFn, }); @@ -706,12 +710,12 @@ describe('MarketOperationUtils tests', () => { it('batches contiguous bridge sources', async () => { const rates: RatesBySource = {}; - rates[ERC20BridgeSource.UniswapV2] = [1, 0.01, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [1, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; - rates[ERC20BridgeSource.CurveUsdcDai] = [0.48, 0.01, 0.01, 0.01]; + rates[ERC20BridgeSource.Curve] = [0.48, 0.01, 0.01, 0.01]; replaceSamplerOps({ - getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), + getSellQuotesAsync: createGetMultipleSellQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync( createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -721,7 +725,7 @@ describe('MarketOperationUtils tests', () => { numSamples: 4, excludedSources: [ ERC20BridgeSource.Kyber, - ..._.without(DEFAULT_OPTS.excludedSources, ERC20BridgeSource.CurveUsdcDai), + ..._.without(DEFAULT_OPTS.excludedSources, ERC20BridgeSource.Curve), ], shouldBatchBridgeOrders: true, }, @@ -729,9 +733,9 @@ describe('MarketOperationUtils tests', () => { expect(improvedOrders).to.be.length(3); const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source)); expect(orderFillSources).to.deep.eq([ - [ERC20BridgeSource.UniswapV2], + [ERC20BridgeSource.Uniswap], [ERC20BridgeSource.Native], - [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.CurveUsdcDai], + [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Curve], ]); }); }); @@ -748,10 +752,10 @@ describe('MarketOperationUtils tests', () => { bridgeSlippage: 0, maxFallbackSlippage: 100, excludedSources: [ - ...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), ERC20BridgeSource.Kyber, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2Eth, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, ], allowFallback: false, shouldBatchBridgeOrders: false, @@ -765,9 +769,9 @@ describe('MarketOperationUtils tests', () => { const numSamples = _.random(1, 16); let actualNumSamples = 0; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { + getBuyQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { actualNumSamples = amounts.length; - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); + return DEFAULT_OPS.getBuyQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -780,9 +784,9 @@ describe('MarketOperationUtils tests', () => { it('polls all DEXes if `excludedSources` is empty', async () => { let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sources.slice(); - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); + getBuyQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { + sourcesPolled = sourcesPolled.concat(sources.slice()); + return DEFAULT_OPS.getBuyQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -798,7 +802,7 @@ describe('MarketOperationUtils tests', () => { DEFAULT_RATES, ); replaceSamplerOps({ - getBuyQuotes: fn, + getBuyQuotesAsync: fn, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -821,9 +825,9 @@ describe('MarketOperationUtils tests', () => { const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length)); let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { - sourcesPolled = sources.slice(); - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); + getBuyQuotesAsync: (sources, makerToken, takerToken, amounts, wethAddress) => { + sourcesPolled = sourcesPolled.concat(sources.slice()); + return DEFAULT_OPS.getBuyQuotesAsync(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -879,7 +883,7 @@ describe('MarketOperationUtils tests', () => { expect(improvedOrders).to.not.be.length(0); for (const order of improvedOrders) { const expectedTakerAmount = order.fills[0].output; - const slippage = order.takerAssetAmount.div(expectedTakerAmount.plus(1)).toNumber() - 1; + const slippage = order.takerAssetAmount.div(expectedTakerAmount.plus(1)).minus(1); assertRoughlyEquals(slippage, bridgeSlippage, 1); } }); @@ -887,10 +891,10 @@ describe('MarketOperationUtils tests', () => { it('can mix convex sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.UniswapV2] = [0.5, 0.05, 0.05, 0.05]; + rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -900,7 +904,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ERC20BridgeSource.Native, ERC20BridgeSource.Native, ]; @@ -915,18 +919,20 @@ describe('MarketOperationUtils tests', () => { const nativeFeeRate = 0.06; const rates: RatesBySource = { [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91] - [ERC20BridgeSource.UniswapV2]: [0.96, 0.1, 0.1, 0.1], + [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], }; const feeSchedule = { - [ERC20BridgeSource.Native]: FILL_AMOUNT.div(4) - .times(nativeFeeRate) - .dividedToIntegerBy(ETH_TO_TAKER_RATE), + [ERC20BridgeSource.Native]: _.constant( + FILL_AMOUNT.div(4) + .times(nativeFeeRate) + .dividedToIntegerBy(ETH_TO_TAKER_RATE), + ), }; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), + getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -935,7 +941,7 @@ describe('MarketOperationUtils tests', () => { ); const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Native, ERC20BridgeSource.Native, @@ -950,17 +956,19 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = { [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.UniswapV2]: [1, 0.7, 0.2, 0.2], + [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], }; const feeSchedule = { - [ERC20BridgeSource.UniswapV2]: FILL_AMOUNT.div(4) - .times(uniswapFeeRate) - .dividedToIntegerBy(ETH_TO_TAKER_RATE), + [ERC20BridgeSource.Uniswap]: _.constant( + FILL_AMOUNT.div(4) + .times(uniswapFeeRate) + .dividedToIntegerBy(ETH_TO_TAKER_RATE), + ), }; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), - getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), + getMedianSellRateAsync: createGetMedianSellRate(ETH_TO_TAKER_RATE), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -971,7 +979,7 @@ describe('MarketOperationUtils tests', () => { const expectedSources = [ ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); }); @@ -979,10 +987,10 @@ describe('MarketOperationUtils tests', () => { it('fallback orders use different sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; - rates[ERC20BridgeSource.UniswapV2] = [0.6, 0.05, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -994,7 +1002,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Native, - ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Uniswap, ]; const secondSources = [ERC20BridgeSource.Eth2Dai]; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); @@ -1004,10 +1012,10 @@ describe('MarketOperationUtils tests', () => { it('does not create a fallback if below maxFallbackSlippage', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.UniswapV2] = [1, 1, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -1015,7 +1023,7 @@ describe('MarketOperationUtils tests', () => { { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, ); const orderSources = improvedOrders.map(o => o.fills[0].source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV2]; + const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; const secondSources: ERC20BridgeSource[] = []; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); @@ -1025,9 +1033,9 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; - rates[ERC20BridgeSource.UniswapV2] = [0.48, 0.47, 0.01, 0.01]; + rates[ERC20BridgeSource.Uniswap] = [0.48, 0.47, 0.01, 0.01]; replaceSamplerOps({ - getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), + getBuyQuotesAsync: createGetMultipleBuyQuotesOperationFromRates(rates), }); const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync( createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), @@ -1042,7 +1050,7 @@ describe('MarketOperationUtils tests', () => { const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source)); expect(orderFillSources).to.deep.eq([ [ERC20BridgeSource.Native], - [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.UniswapV2], + [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap], ]); }); }); @@ -1078,7 +1086,7 @@ describe('MarketOperationUtils tests', () => { }; const orders = [smallOrder, largeOrder]; const feeSchedule = { - [ERC20BridgeSource.Native]: new BigNumber(2e5), + [ERC20BridgeSource.Native]: _.constant(2e5), }; it('penalizes native fill based on target amount when target is smaller', () => { diff --git a/packages/asset-swapper/test/quote_simulation_test.ts b/packages/asset-swapper/test/quote_simulation_test.ts index 538990fd6a..8ec591e687 100644 --- a/packages/asset-swapper/test/quote_simulation_test.ts +++ b/packages/asset-swapper/test/quote_simulation_test.ts @@ -22,7 +22,7 @@ describe('quote_simulation tests', async () => { const TAKER_TOKEN = randomAddress(); const DEFAULT_MAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(MAKER_TOKEN); const DEFAULT_TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN); - const GAS_SCHEDULE = { [ERC20BridgeSource.Native]: 1 }; + const GAS_SCHEDULE = { [ERC20BridgeSource.Native]: _.constant(1) }; // Check if two numbers are within `maxError` error rate within each other (default 1 bps). function assertRoughlyEquals(n1: BigNumber, n2: BigNumber, maxError: BigNumber | number = 1e-12): void { diff --git a/packages/asset-swapper/test/utils/mock_balancer_pools_cache.ts b/packages/asset-swapper/test/utils/mock_balancer_pools_cache.ts new file mode 100644 index 0000000000..fabfdbd986 --- /dev/null +++ b/packages/asset-swapper/test/utils/mock_balancer_pools_cache.ts @@ -0,0 +1,24 @@ +import { BalancerPool, BalancerPoolsCache } from '../../src/utils/market_operation_utils/balancer_utils'; + +export interface Handlers { + getPoolsForPairAsync: (takerToken: string, makerToken: string) => Promise; + _fetchPoolsForPairAsync: (takerToken: string, makerToken: string) => Promise; +} + +export class MockBalancerPoolsCache extends BalancerPoolsCache { + constructor(public handlers: Partial) { + super(); + } + + public async getPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + return this.handlers.getPoolsForPairAsync + ? this.handlers.getPoolsForPairAsync(takerToken, makerToken) + : super.getPoolsForPairAsync(takerToken, makerToken); + } + + protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + return this.handlers._fetchPoolsForPairAsync + ? this.handlers._fetchPoolsForPairAsync(takerToken, makerToken) + : super._fetchPoolsForPairAsync(takerToken, makerToken); + } +} diff --git a/packages/asset-swapper/test/utils/mock_sampler_contract.ts b/packages/asset-swapper/test/utils/mock_sampler_contract.ts index 49f590b0d5..cca7cc59b3 100644 --- a/packages/asset-swapper/test/utils/mock_sampler_contract.ts +++ b/packages/asset-swapper/test/utils/mock_sampler_contract.ts @@ -226,6 +226,9 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract { } private _callEncodedFunction(callData: string): string { + if (callData === '0x') { + return callData; + } // tslint:disable-next-line: custom-no-magic-numbers const selector = hexUtils.slice(callData, 0, 4); for (const [name, handler] of Object.entries(this._handlers)) { diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 5bf60560a1..078641b241 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -25,6 +25,10 @@ { "note": "Update ganache snapshot Exchange Proxy addresses for MetaTransactions", "pr": 2610 + }, + { + "note": "Add BalancerBridge addresses", + "pr": 2613 } ] }, diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 3aa6bccc5b..451361b286 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -33,6 +33,7 @@ "maximumGasPrice": "0xe2bfd35306495d11e3c9db0d8de390cda24563cf", "dexForwarderBridge": "0xc47b7094f378e54347e281aab170e8cca69d880a", "multiBridge": "0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1", + "balancerBridge": "0xfe01821ca163844203220cd08e4f2b2fb43ae4e4", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", @@ -79,6 +80,7 @@ "maximumGasPrice": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a", "dexForwarderBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1", "multiBridge": "0x0000000000000000000000000000000000000000", + "balancerBridge": "0x47697b44bd89051e93b4d5857ba8e024800a74ac", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", @@ -125,6 +127,7 @@ "maximumGasPrice": "0x47697b44bd89051e93b4d5857ba8e024800a74ac", "dexForwarderBridge": "0x0000000000000000000000000000000000000000", "multiBridge": "0x0000000000000000000000000000000000000000", + "balancerBridge": "0x5d8c9ba74607d2cbc4176882a42d4ace891c1c00", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", @@ -171,6 +174,7 @@ "maximumGasPrice": "0x67a094cf028221ffdd93fc658f963151d05e2a74", "dexForwarderBridge": "0xf220eb0b29e18bbc8ebc964e915b7547c7b4de4f", "multiBridge": "0x0000000000000000000000000000000000000000", + "balancerBridge": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", @@ -217,6 +221,7 @@ "maximumGasPrice": "0x0000000000000000000000000000000000000000", "dexForwarderBridge": "0x0000000000000000000000000000000000000000", "multiBridge": "0x0000000000000000000000000000000000000000", + "balancerBridge": "0x0000000000000000000000000000000000000000", "exchangeProxyGovernor": "0x0000000000000000000000000000000000000000", "exchangeProxy": "0x2ebb94cc79d7d0f1195300aaf191d118f53292a8", "exchangeProxyAllowanceTarget": "0x3eab3df72fd584b50184ff7d988a0d8f9328c866", diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 56061e001a..cba8c904db 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -34,6 +34,7 @@ export interface ContractAddresses { maximumGasPrice: string; dexForwarderBridge: string; multiBridge: string; + balancerBridge: string; exchangeProxyGovernor: string; exchangeProxy: string; exchangeProxyAllowanceTarget: string; diff --git a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json index 5ff9f3bebf..39f8d7381f 100644 --- a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json @@ -473,10 +473,10 @@ }, "evm": { "bytecode": { - "object": "0x60806040523480156200001157600080fd5b5060405162003d9738038062003d9783398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b613cfd806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106101365760003560e01c806368be3cf2116100b25780639f76ad3511610081578063c7f7142e11610066578063c7f7142e1461027b578063d0eea06d1461029b578063e68248f7146102ae57610136565b80639f76ad3514610255578063abffc7611461026857610136565b806368be3cf2146101fc5780636dd6b78d1461021c5780638b123a021461022f57806398cdafba1461024257610136565b80634cb8e2531161010957806359f515d0116100ee57806359f515d0146101c357806360ee052a146101d657806364ee6ade146101e957610136565b80634cb8e2531461019d57806358306ba0146101b057610136565b80631796fb871461013b5780632d753aa414610164578063354152a3146101775780634703a7e61461018a575b600080fd5b61014e6101493660046133ca565b6102c1565b60405161015b919061399f565b60405180910390f35b61014e61017236600461317a565b610492565b61014e6101853660046133ca565b610688565b61014e6101983660046132f8565b610842565b61014e6101ab3660046132f8565b610a1e565b61014e6101be366004613200565b610c38565b61014e6101d1366004613537565b610ccf565b61014e6101e43660046132f8565b610d78565b61014e6101f73660046132f8565b611038565b61020f61020a3660046134cb565b6111fe565b60405161015b9190613921565b61014e61022a3660046132f8565b61133d565b61014e61023d366004613537565b6115e5565b61014e610250366004613273565b6118be565b61014e610263366004613200565b6118f8565b61014e610276366004613408565b611afc565b61028e610289366004613130565b611cdf565b60405161015b91906137c1565b61014e6102a9366004613358565b611e1a565b61014e6102bc366004613408565b611e4b565b6060600082519050806040519080825280602002602001820160405280156102f3578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061035057fe5b602002602001015160405160240161036a939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516103f391906137a5565b6000604051808303818686fa925050503d806000811461042f576040519150601f19603f3d011682016040523d82523d6000602084013e610434565b606091505b5090925090506000821561045d578180602001905161045691908101906136de565b9050610465565b505050610488565b8086858151811061047257fe5b60209081029190910101525050506001016102f9565b5050949350505050565b6060600082519050806040519080825280602002602001820160405280156104c4578160200160208202803883390190505b50915073ffffffffffffffffffffffffffffffffffffffff87166104e8575061067f565b60005b8181101561067c57600060608973ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff16636e79e133905060e01b8b8b8b8b898151811061054357fe5b602002602001015160405160240161055e949392919061384a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e791906137a5565b6000604051808303818686fa925050503d8060008114610623576040519150601f19603f3d011682016040523d82523d6000602084013e610628565b606091505b50909250905060008215610651578180602001905161064a91908101906136de565b9050610659565b50505061067c565b8086858151811061066657fe5b60209081029190910101525050506001016104eb565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156106ba578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061071757fe5b6020026020010151604051602401610731939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107ba91906137a5565b6000604051808303818686fa925050503d80600081146107f6576040519150601f19603f3d011682016040523d82523d6000602084013e6107fb565b606091505b5090925090506000821561045d578180602001905161081d91908101906136de565b90508086858151811061082c57fe5b60209081029190910101525050506001016106c0565b606061084e838561201e565b8151604080518281526020808402820101909152818015610879578160200160208202803883390190505b50915060005b81811015610a155760006060610893612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a88815181106108dd57fe5b60200260200101516040516024016108f7939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098091906137a5565b6000604051808303818686fa925050503d80600081146109bc576040519150601f19603f3d011682016040523d82523d6000602084013e6109c1565b606091505b509092509050600082156109ea57818060200190516109e391908101906136de565b90506109f2565b505050610a15565b808685815181106109ff57fe5b602090810291909101015250505060010161087f565b50509392505050565b6060610a2a838561201e565b8151604080518281526020808402820101909152818015610a55578160200160208202803883390190505b5091506000610a626120a9565b9050600080805b84811015610c2c578373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480610ad657508373ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610b6e57610af98989898481518110610aec57fe5b60200260200101516120c1565b909350915073ffffffffffffffffffffffffffffffffffffffff82167331e085afd48a1d6e51cc193153d625e8f0514c7f1480610b5f575073ffffffffffffffffffffffffffffffffffffffff8216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b15610b6957600092505b610c0c565b610b7f8985898481518110610aec57fe5b90935091508215610c0c576000610b97858a866120c1565b909450905073ffffffffffffffffffffffffffffffffffffffff83167331e085afd48a1d6e51cc193153d625e8f0514c7f148015610c0057508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b15610c0a57600093505b505b82868281518110610c1957fe5b6020908102919091010152600101610a69565b50505050509392505050565b60608273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480610c9f57508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b15610ca957610cc7565b6060610cb6868585611038565b9050610cc3848683611038565b9150505b949350505050565b6060610cdb83836115e5565b905060005b8351811015610d7157818181518110610cf557fe5b6020026020010151600014610d6957610d50828281518110610d1357fe5b6020026020010151858381518110610d2757fe5b602002602001015160a00151868481518110610d3f57fe5b602002602001015160800151612435565b828281518110610d5c57fe5b6020026020010181815250505b600101610ce0565b5092915050565b6060610d84838561201e565b8151604080518281526020808402820101909152818015610daf578160200160208202803883390190505b5091506000610dbc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610dfc57610df786612477565b610dff565b60005b90506000610e0b6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610e4b57610e4686612477565b610e4e565b60005b905060005b8381101561102d576001610e656120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610efc578651610edb9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b602002602001015161250f565b878481518110610ee757fe5b60200260200101819350828152505050611019565b610f046120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610f6d578651610edb9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b8651600090610fa69085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b925090508015610ffc57610fdb857f2640f62c000000000000000000000000000000000000000000000000000000008361250f565b888581518110610fe757fe5b60200260200101819450828152505050611017565b600087848151811061100a57fe5b6020026020010181815250505b505b80611024575061102d565b50600101610e53565b505050509392505050565b6060611044838561201e565b815160408051828152602080840282010190915281801561106f578160200160208202803883390190505b50915060005b81811015610a155760006060611089612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a88815181106110d357fe5b60200260200101516040516024016110ed939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161117691906137a5565b6000604051808303818686fa925050503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b509092509050600082156109ea57818060200190516111d991908101906136de565b9050808685815181106111e857fe5b6020908102919091010152505050600101611075565b60408051828152602080840282010190915260609082801561123457816020015b606081526020019060019003908161121f5790505b50905060005b808314610d7157600060603086868581811061125257fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261128c57600080fd5b9091016020810191503567ffffffffffffffff8111156112ab57600080fd5b368190038213156112bb57600080fd5b6040516112c9929190613795565b600060405180830381855afa9150503d8060008114611304576040519150601f19603f3d011682016040523d82523d6000602084013e611309565b606091505b50915091508161131b57805160208201fd5b8084848151811061132857fe5b6020908102919091010152505060010161123a565b6060611349838561201e565b8151604080518281526020808402820101909152818015611374578160200160208202803883390190505b50915060006113816120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146113c1576113bc86612477565b6113c4565b60005b905060006113d06120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146114105761140b86612477565b611413565b60005b905060005b8381101561102d57600161142a6120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156114b45786516114939085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b87848151811061149f57fe5b602002602001018193508281525050506115d1565b6114bc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156115255786516114939084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b865160009061155e9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b9250905080156115b457611593847fcd7724c3000000000000000000000000000000000000000000000000000000008361250f565b88858151811061159f57fe5b602002602001018194508281525050506115cf565b60008784815181106115c257fe5b6020026020010181815250505b505b806115dc575061102d565b50600101611418565b60608251604051908082528060200260200182016040528015611612578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b845181146118b65783818151811061164857fe5b60200260200101515160001480611676575084818151811061166657fe5b6020026020010151608001516000145b80611698575084818151811061168857fe5b602002602001015160a001516000145b156116bc5760008382815181106116ab57fe5b6020026020010181815250506118ae565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061170857fe5b602002602001015189878151811061171c57fe5b6020026020010151604051602401611735929190613a5d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117be91906137a5565b6000604051808303818686fa925050503d80600081146117fa576040519150601f19603f3d011682016040523d82523d6000602084013e6117ff565b606091505b50915091508161182a57600085848151811061181757fe5b60200260200101818152505050506118ae565b611832612dce565b600080838060200190516118499190810190613671565b9194509250905060038351600681111561185f57fe5b14158061186a575080155b1561188e57600088878151811061187d57fe5b6020026020010181815250506118a8565b8188878151811061189b57fe5b6020026020010181815250505b50505050505b600101611634565b505092915050565b60606118ee858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b612657565b9695505050505050565b60606000825190508060405190808252806020026020018201604052801561192a578160200160208202803883390190505b509150600061193a878787611cdf565b905073ffffffffffffffffffffffffffffffffffffffff811661195f5750610cc79050565b60005b82811015611af157600060608373ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b88815181106119b957fe5b60200260200101516040516024016119d3939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611a5c91906137a5565b6000604051808303818686fa925050503d8060008114611a98576040519150601f19603f3d011682016040523d82523d6000602084013e611a9d565b606091505b50909250905060008215611ac65781806020019051611abf91908101906136de565b9050611ace565b505050611af1565b80878581518110611adb57fe5b6020908102919091010152505050600101611962565b505050949350505050565b606060008251905080604051908082528060200260200182016040528015611b2e578160200160208202803883390190505b50915060005b818110156118b65760006060611b4861281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff1663d06ca61f905060e01b888681518110611b9057fe5b60200260200101518a604051602401611baa929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c3391906137a5565b6000604051808303818686fa925050503d8060008114611c6f576040519150601f19603f3d011682016040523d82523d6000602084013e611c74565b606091505b50909250905060008215611cb45781806020019051611c9691908101906135de565b600189510381518110611ca557fe5b60200260200101519050611cbc565b5050506118b6565b80868581518110611cc957fe5b6020908102919091010152505050600101611b34565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611d1a90869086906024016137e2565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611da291906137a5565b600060405180830381855afa9150503d8060008114611ddd576040519150601f19603f3d011682016040523d82523d6000602084013e611de2565b606091505b5091509150818015611df5575080516020145b15611e0f57611e0581600c612835565b9350505050611e13565b5050505b9392505050565b606061067f858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000612657565b606060008251905080604051908082528060200260200182016040528015611e7d578160200160208202803883390190505b50915060005b818110156118b65760006060611e9761281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff16631f00ca74905060e01b888681518110611edf57fe5b60200260200101518a604051602401611ef9929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611f8291906137a5565b6000604051808303818686fa925050503d8060008114611fbe576040519150601f19603f3d011682016040523d82523d6000602084013e611fc3565b606091505b50909250905060008215611cb45781806020019051611fe591908101906135de565b600081518110611ff157fe5b602002602001015190508086858151811061200857fe5b6020908102919091010152505050600101611e83565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561208d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161208490613a00565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006120ce6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614612106578561211c565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121286120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146121605785612176565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121838861287a565b60ff16905060006121938861287a565b60ff169050600060606121a4612885565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4f61ff8b00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff929092169162061a8091612224916137a5565b6000604051808303818686fa925050503d8060008114612260576040519150601f19603f3d011682016040523d82523d6000602084013e612265565b606091505b50915091508161228257506000965086955061242d945050505050565b60008180602001905161229891908101906130e7565b90508073ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff16630c235d96905060e01b89898e60006040516024016122ef94939291906138eb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161237891906137a5565b6000604051808303818686fa925050503d80600081146123b4576040519150601f19603f3d011682016040523d82523d6000602084013e6123b9565b606091505b50909350915060008084156123e557838060200190516123dc9190810190613103565b925090506123fb565b506000995089985061242d975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e8502028161241757fe5b048161241f57fe5b049a50985050505050505050505b935093915050565b6000610cc78361246b61244f82600163ffffffff61289d16565b61245f888763ffffffff6128bc16565b9063ffffffff6128ed16565b9063ffffffff61290916565b6000612481612933565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016124b991906137c1565b60206040518083038186803b1580156124d157600080fd5b505afa1580156124e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061250991908101906130e7565b92915050565b60008073ffffffffffffffffffffffffffffffffffffffff85166125325761242d565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016125619190613bb1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516125ea91906137a5565b6000604051808303818686fa925050503d8060008114612626576040519150601f19603f3d011682016040523d82523d6000602084013e61262b565b606091505b509092509050811561264e578080602001905161264b91908101906136de565b92505b50935093915050565b6060612663868861201e565b845161266e576118ee565b6000806000875160405190808252806020026020018201604052801561269e578160200160208202803883390190505b5093506126c2898b8a6000815181106126b357fe5b6020026020010151898961294b565b9250826126d257506118ee915050565b6126df8a8a85898961294b565b9150816126ef57506118ee915050565b60005b885181101561280f5760005b88602001518110156127d1576127288a838151811061271957fe5b60200260200101518587612435565b945061273f89600001516127100161271087612435565b945060006127508d8d888c8c61294b565b90508061275d57506127d1565b8094508a838151811061276c57fe5b602002602001015185106127c85760008b848151811061278857fe5b60200260200101518c858151811061279c57fe5b6020026020010151870361271002816127b157fe5b0490508a6000015181116127c65750506127d1565b505b506001016126fe565b506127f08982815181106127e157fe5b60200260200101518486612435565b8582815181106127fc57fe5b60209081029190910101526001016126f2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561285b5761285b6128566004855185601401612c00565b612ca5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600061250982612cad565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000828211156128b6576128b661285660028585612d7e565b50900390565b6000826128cb57506000612509565b828202828482816128d857fe5b0414611e1357611e1361285660018686612d7e565b600082820183811015611e1357611e1361285660008686612d7e565b60008161291f5761291f61285660038585612d7e565b600082848161292a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061298057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e253000000000000000000000000000000000000000000000000000000001415612a90576040517f4cb8e2530000000000000000000000000000000000000000000000000000000090612a0c908a908a908590602401613881565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612b49565b6040517f9f76ad350000000000000000000000000000000000000000000000000000000090612ac99086908b908b908690602401613809565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff1684604051612b7291906137a5565b600060405180830381855afa9150503d8060008114612bad576040519150601f19603f3d011682016040523d82523d6000602084013e612bb2565b606091505b509150915081612bc957600094505050505061067f565b80806020019051612bdd91908101906135de565b600081518110612be957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b848484604051602401612c1f939291906139d4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051612d1191906137a5565b600060405180830381855afa9150503d8060008114612d4c576040519150601f19603f3d011682016040523d82523d6000602084013e612d51565b606091505b5091509150818015612d64575080516020145b15612d7757612d74816000612d9d565b92505b5050919050565b606063e946c1bb60e01b848484604051602401612c1f939291906139b2565b6000611e13838360008160200183511015612dc557612dc56128566005855185602001612c00565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b803561250981613c95565b600082601f830112612e0b578081fd5b8135612e1e612e1982613c45565b613c1e565b8181529150602080830190840160005b83811015612e5b57612e468760208435890101612ece565b83526020928301929190910190600101612e2e565b5050505092915050565b600082601f830112612e75578081fd5b8135612e83612e1982613c45565b818152915060208083019084810181840286018201871015612ea457600080fd5b60005b84811015612ec357813584529282019290820190600101612ea7565b505050505092915050565b600082601f830112612ede578081fd5b813567ffffffffffffffff811115612ef4578182fd5b612f2560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c1e565b9150808252836020828501011115612f3c57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b811461250957600080fd5b600060408284031215612f78578081fd5b612f826040613c1e565b9050813581526020820135602082015292915050565b60006101c0808385031215612fab578182fd5b612fb481613c1e565b915050612fc18383612df0565b8152612fd08360208401612df0565b6020820152612fe28360408401612df0565b6040820152612ff48360608401612df0565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561305657600080fd5b61306286838701612ece565b8385015261016092508285013591508082111561307e57600080fd5b61308a86838701612ece565b838501526101809250828501359150808211156130a657600080fd5b6130b286838701612ece565b838501526101a09250828501359150808211156130ce57600080fd5b506130db85828601612ece565b82840152505092915050565b6000602082840312156130f8578081fd5b8151611e1381613c95565b60008060408385031215613115578081fd5b825161312081613c95565b6020939093015192949293505050565b600080600060608486031215613144578081fd5b833561314f81613c95565b9250602084013561315f81613c95565b9150604084013561316f81613c95565b809150509250925092565b600080600080600060a08688031215613191578283fd5b853561319c81613c95565b945060208601356131ac81613c95565b935060408601356131bc81613c95565b925060608601356131cc81613c95565b9150608086013567ffffffffffffffff8111156131e7578182fd5b6131f388828901612e65565b9150509295509295909350565b60008060008060808587031215613215578182fd5b843561322081613c95565b9350602085013561323081613c95565b9250604085013561324081613c95565b9150606085013567ffffffffffffffff81111561325b578182fd5b61326787828801612e65565b91505092959194509250565b600080600080600060c0868803121561328a578283fd5b853561329581613c95565b945060208601356132a581613c95565b935060408601356132b581613c95565b9250606086013567ffffffffffffffff8111156132d0578182fd5b6132dc88828901612e65565b9250506132ec8760808801612f67565b90509295509295909350565b60008060006060848603121561330c578081fd5b833561331781613c95565b9250602084013561332781613c95565b9150604084013567ffffffffffffffff811115613342578182fd5b61334e86828701612e65565b9150509250925092565b60008060008060a0858703121561336d578182fd5b843561337881613c95565b9350602085013561338881613c95565b9250604085013567ffffffffffffffff8111156133a3578283fd5b6133af87828801612e65565b9250506133bf8660608701612f67565b905092959194509250565b600080600080608085870312156133df578182fd5b84356133ea81613c95565b93506133f98660208701612f55565b92506132408660408701612f55565b6000806040838503121561341a578182fd5b823567ffffffffffffffff80821115613431578384fd5b81850186601f820112613442578485fd5b80359250613452612e1984613c45565b80848252602080830192508084018a828389028701011115613472578889fd5b8894505b8685101561349d57803561348981613c95565b845260019490940193928101928101613476565b5090965087013593505050808211156134b4578283fd5b506134c185828601612e65565b9150509250929050565b600080602083850312156134dd578182fd5b823567ffffffffffffffff808211156134f4578384fd5b81850186601f820112613505578485fd5b8035925081831115613515578485fd5b8660208085028301011115613528578485fd5b60200196919550909350505050565b60008060408385031215613549578182fd5b823567ffffffffffffffff80821115613560578384fd5b81850186601f820112613571578485fd5b80359250613581612e1984613c45565b83815260208082019190838101885b878110156135b9576135a78c848435890101612f98565b85529382019390820190600101613590565b509197508801359450505050808211156135d1578283fd5b506134c185828601612dfb565b600060208083850312156135f0578182fd5b825167ffffffffffffffff811115613606578283fd5b80840185601f820112613617578384fd5b80519150613627612e1983613c45565b8281528381019082850185850284018601891015613643578687fd5b8693505b84841015613665578051835260019390930192918501918501613647565b50979650505050505050565b600080600083850360a0811215613686578182fd5b6060811215613693578182fd5b5061369e6060613c1e565b8451600781106136ac578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461316f578182fd5b6000602082840312156136ef578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015613741578151865260209586019590910190600101613723565b5093949350505050565b60008151808452613763816020860160208601613c65565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516137b7818460208701613c65565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152808516604084015250608060608301526118ee6080830184613710565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80861683528085166020840152506060604083015261067f6060830184613710565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015613992577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261398085835161374b565b94509285019290850190600101613946565b5092979650505050505050565b600060208252611e136020830184613710565b60608101600485106139c057fe5b938152602081019290925260409091015290565b60608101600885106139c057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252613a716040830185516136f6565b6020840151613a8360608401826136f6565b506040840151613a9660808401826136f6565b506060840151613aa960a08401826136f6565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152613b1a61020087018561374b565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152613b59828761374b565b838b015196508489820301868a0152613b72818861374b565b955050808a0151955050505080858303016101e086015250613b94818361374b565b8481036020860152613ba6818761374b565b979650505050505050565b90815260200190565b60006040820184835260406020840152808451808352606085019150602086019250835b81811015613c1257835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101613bde565b50909695505050505050565b60405181810167ffffffffffffffff81118282101715613c3d57600080fd5b604052919050565b600067ffffffffffffffff821115613c5b578081fd5b5060209081020190565b60005b83811015613c80578181015183820152602001613c68565b83811115613c8f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613cb757600080fd5b5056fea365627a7a72315820a87fc7253056a246e4a91d483bd03f17a76820224b3bf36e5df9de67803770596c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x60806040523480156200001157600080fd5b5060405162003d9738038062003d9783398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b613cfd806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106101365760003560e01c806368be3cf2116100b25780639f76ad3511610081578063c7f7142e11610066578063c7f7142e1461027b578063d0eea06d1461029b578063e68248f7146102ae57610136565b80639f76ad3514610255578063abffc7611461026857610136565b806368be3cf2146101fc5780636dd6b78d1461021c5780638b123a021461022f57806398cdafba1461024257610136565b80634cb8e2531161010957806359f515d0116100ee57806359f515d0146101c357806360ee052a146101d657806364ee6ade146101e957610136565b80634cb8e2531461019d57806358306ba0146101b057610136565b80631796fb871461013b5780632d753aa414610164578063354152a3146101775780634703a7e61461018a575b600080fd5b61014e6101493660046133ca565b6102c1565b60405161015b919061399f565b60405180910390f35b61014e61017236600461317a565b610492565b61014e6101853660046133ca565b610688565b61014e6101983660046132f8565b610842565b61014e6101ab3660046132f8565b610a1e565b61014e6101be366004613200565b610c38565b61014e6101d1366004613537565b610ccf565b61014e6101e43660046132f8565b610d78565b61014e6101f73660046132f8565b611038565b61020f61020a3660046134cb565b6111fe565b60405161015b9190613921565b61014e61022a3660046132f8565b61133d565b61014e61023d366004613537565b6115e5565b61014e610250366004613273565b6118be565b61014e610263366004613200565b6118f8565b61014e610276366004613408565b611afc565b61028e610289366004613130565b611cdf565b60405161015b91906137c1565b61014e6102a9366004613358565b611e1a565b61014e6102bc366004613408565b611e4b565b6060600082519050806040519080825280602002602001820160405280156102f3578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061035057fe5b602002602001015160405160240161036a939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516103f391906137a5565b6000604051808303818686fa925050503d806000811461042f576040519150601f19603f3d011682016040523d82523d6000602084013e610434565b606091505b5090925090506000821561045d578180602001905161045691908101906136de565b9050610465565b505050610488565b8086858151811061047257fe5b60209081029190910101525050506001016102f9565b5050949350505050565b6060600082519050806040519080825280602002602001820160405280156104c4578160200160208202803883390190505b50915073ffffffffffffffffffffffffffffffffffffffff87166104e8575061067f565b60005b8181101561067c57600060608973ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff16636e79e133905060e01b8b8b8b8b898151811061054357fe5b602002602001015160405160240161055e949392919061384a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e791906137a5565b6000604051808303818686fa925050503d8060008114610623576040519150601f19603f3d011682016040523d82523d6000602084013e610628565b606091505b50909250905060008215610651578180602001905161064a91908101906136de565b9050610659565b50505061067c565b8086858151811061066657fe5b60209081029190910101525050506001016104eb565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156106ba578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061071757fe5b6020026020010151604051602401610731939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107ba91906137a5565b6000604051808303818686fa925050503d80600081146107f6576040519150601f19603f3d011682016040523d82523d6000602084013e6107fb565b606091505b5090925090506000821561045d578180602001905161081d91908101906136de565b90508086858151811061082c57fe5b60209081029190910101525050506001016106c0565b606061084e838561201e565b8151604080518281526020808402820101909152818015610879578160200160208202803883390190505b50915060005b81811015610a155760006060610893612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a88815181106108dd57fe5b60200260200101516040516024016108f7939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098091906137a5565b6000604051808303818686fa925050503d80600081146109bc576040519150601f19603f3d011682016040523d82523d6000602084013e6109c1565b606091505b509092509050600082156109ea57818060200190516109e391908101906136de565b90506109f2565b505050610a15565b808685815181106109ff57fe5b602090810291909101015250505060010161087f565b50509392505050565b6060610a2a838561201e565b8151604080518281526020808402820101909152818015610a55578160200160208202803883390190505b5091506000610a626120a9565b9050600080805b84811015610c2c578373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480610ad657508373ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610b6e57610af98989898481518110610aec57fe5b60200260200101516120c1565b909350915073ffffffffffffffffffffffffffffffffffffffff82167331e085afd48a1d6e51cc193153d625e8f0514c7f1480610b5f575073ffffffffffffffffffffffffffffffffffffffff8216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b15610b6957600092505b610c0c565b610b7f8985898481518110610aec57fe5b90935091508215610c0c576000610b97858a866120c1565b909450905073ffffffffffffffffffffffffffffffffffffffff83167331e085afd48a1d6e51cc193153d625e8f0514c7f148015610c0057508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b15610c0a57600093505b505b82868281518110610c1957fe5b6020908102919091010152600101610a69565b50505050509392505050565b60608273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480610c9f57508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b15610ca957610cc7565b6060610cb6868585611038565b9050610cc3848683611038565b9150505b949350505050565b6060610cdb83836115e5565b905060005b8351811015610d7157818181518110610cf557fe5b6020026020010151600014610d6957610d50828281518110610d1357fe5b6020026020010151858381518110610d2757fe5b602002602001015160a00151868481518110610d3f57fe5b602002602001015160800151612435565b828281518110610d5c57fe5b6020026020010181815250505b600101610ce0565b5092915050565b6060610d84838561201e565b8151604080518281526020808402820101909152818015610daf578160200160208202803883390190505b5091506000610dbc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610dfc57610df786612477565b610dff565b60005b90506000610e0b6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610e4b57610e4686612477565b610e4e565b60005b905060005b8381101561102d576001610e656120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610efc578651610edb9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b602002602001015161250f565b878481518110610ee757fe5b60200260200101819350828152505050611019565b610f046120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610f6d578651610edb9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b8651600090610fa69085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b925090508015610ffc57610fdb857f2640f62c000000000000000000000000000000000000000000000000000000008361250f565b888581518110610fe757fe5b60200260200101819450828152505050611017565b600087848151811061100a57fe5b6020026020010181815250505b505b80611024575061102d565b50600101610e53565b505050509392505050565b6060611044838561201e565b815160408051828152602080840282010190915281801561106f578160200160208202803883390190505b50915060005b81811015610a155760006060611089612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a88815181106110d357fe5b60200260200101516040516024016110ed939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161117691906137a5565b6000604051808303818686fa925050503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b509092509050600082156109ea57818060200190516111d991908101906136de565b9050808685815181106111e857fe5b6020908102919091010152505050600101611075565b60408051828152602080840282010190915260609082801561123457816020015b606081526020019060019003908161121f5790505b50905060005b808314610d7157600060603086868581811061125257fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261128c57600080fd5b9091016020810191503567ffffffffffffffff8111156112ab57600080fd5b368190038213156112bb57600080fd5b6040516112c9929190613795565b600060405180830381855afa9150503d8060008114611304576040519150601f19603f3d011682016040523d82523d6000602084013e611309565b606091505b50915091508161131b57805160208201fd5b8084848151811061132857fe5b6020908102919091010152505060010161123a565b6060611349838561201e565b8151604080518281526020808402820101909152818015611374578160200160208202803883390190505b50915060006113816120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146113c1576113bc86612477565b6113c4565b60005b905060006113d06120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146114105761140b86612477565b611413565b60005b905060005b8381101561102d57600161142a6120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156114b45786516114939085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b87848151811061149f57fe5b602002602001018193508281525050506115d1565b6114bc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156115255786516114939084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b865160009061155e9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b9250905080156115b457611593847fcd7724c3000000000000000000000000000000000000000000000000000000008361250f565b88858151811061159f57fe5b602002602001018194508281525050506115cf565b60008784815181106115c257fe5b6020026020010181815250505b505b806115dc575061102d565b50600101611418565b60608251604051908082528060200260200182016040528015611612578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b845181146118b65783818151811061164857fe5b60200260200101515160001480611676575084818151811061166657fe5b6020026020010151608001516000145b80611698575084818151811061168857fe5b602002602001015160a001516000145b156116bc5760008382815181106116ab57fe5b6020026020010181815250506118ae565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061170857fe5b602002602001015189878151811061171c57fe5b6020026020010151604051602401611735929190613a5d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117be91906137a5565b6000604051808303818686fa925050503d80600081146117fa576040519150601f19603f3d011682016040523d82523d6000602084013e6117ff565b606091505b50915091508161182a57600085848151811061181757fe5b60200260200101818152505050506118ae565b611832612dce565b600080838060200190516118499190810190613671565b9194509250905060038351600681111561185f57fe5b14158061186a575080155b1561188e57600088878151811061187d57fe5b6020026020010181815250506118a8565b8188878151811061189b57fe5b6020026020010181815250505b50505050505b600101611634565b505092915050565b60606118ee858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b612657565b9695505050505050565b60606000825190508060405190808252806020026020018201604052801561192a578160200160208202803883390190505b509150600061193a878787611cdf565b905073ffffffffffffffffffffffffffffffffffffffff811661195f5750610cc79050565b60005b82811015611af157600060608373ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b88815181106119b957fe5b60200260200101516040516024016119d3939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611a5c91906137a5565b6000604051808303818686fa925050503d8060008114611a98576040519150601f19603f3d011682016040523d82523d6000602084013e611a9d565b606091505b50909250905060008215611ac65781806020019051611abf91908101906136de565b9050611ace565b505050611af1565b80878581518110611adb57fe5b6020908102919091010152505050600101611962565b505050949350505050565b606060008251905080604051908082528060200260200182016040528015611b2e578160200160208202803883390190505b50915060005b818110156118b65760006060611b4861281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff1663d06ca61f905060e01b888681518110611b9057fe5b60200260200101518a604051602401611baa929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c3391906137a5565b6000604051808303818686fa925050503d8060008114611c6f576040519150601f19603f3d011682016040523d82523d6000602084013e611c74565b606091505b50909250905060008215611cb45781806020019051611c9691908101906135de565b600189510381518110611ca557fe5b60200260200101519050611cbc565b5050506118b6565b80868581518110611cc957fe5b6020908102919091010152505050600101611b34565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611d1a90869086906024016137e2565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611da291906137a5565b600060405180830381855afa9150503d8060008114611ddd576040519150601f19603f3d011682016040523d82523d6000602084013e611de2565b606091505b5091509150818015611df5575080516020145b15611e0f57611e0581600c612835565b9350505050611e13565b5050505b9392505050565b606061067f858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000612657565b606060008251905080604051908082528060200260200182016040528015611e7d578160200160208202803883390190505b50915060005b818110156118b65760006060611e9761281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff16631f00ca74905060e01b888681518110611edf57fe5b60200260200101518a604051602401611ef9929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611f8291906137a5565b6000604051808303818686fa925050503d8060008114611fbe576040519150601f19603f3d011682016040523d82523d6000602084013e611fc3565b606091505b50909250905060008215611cb45781806020019051611fe591908101906135de565b600081518110611ff157fe5b602002602001015190508086858151811061200857fe5b6020908102919091010152505050600101611e83565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561208d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161208490613a00565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006120ce6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614612106578561211c565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121286120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146121605785612176565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121838861287a565b60ff16905060006121938861287a565b60ff169050600060606121a4612885565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4f61ff8b00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff929092169162061a8091612224916137a5565b6000604051808303818686fa925050503d8060008114612260576040519150601f19603f3d011682016040523d82523d6000602084013e612265565b606091505b50915091508161228257506000965086955061242d945050505050565b60008180602001905161229891908101906130e7565b90508073ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff16630c235d96905060e01b89898e60006040516024016122ef94939291906138eb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161237891906137a5565b6000604051808303818686fa925050503d80600081146123b4576040519150601f19603f3d011682016040523d82523d6000602084013e6123b9565b606091505b50909350915060008084156123e557838060200190516123dc9190810190613103565b925090506123fb565b506000995089985061242d975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e8502028161241757fe5b048161241f57fe5b049a50985050505050505050505b935093915050565b6000610cc78361246b61244f82600163ffffffff61289d16565b61245f888763ffffffff6128bc16565b9063ffffffff6128ed16565b9063ffffffff61290916565b6000612481612933565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016124b991906137c1565b60206040518083038186803b1580156124d157600080fd5b505afa1580156124e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061250991908101906130e7565b92915050565b60008073ffffffffffffffffffffffffffffffffffffffff85166125325761242d565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016125619190613bb1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516125ea91906137a5565b6000604051808303818686fa925050503d8060008114612626576040519150601f19603f3d011682016040523d82523d6000602084013e61262b565b606091505b509092509050811561264e578080602001905161264b91908101906136de565b92505b50935093915050565b6060612663868861201e565b845161266e576118ee565b6000806000875160405190808252806020026020018201604052801561269e578160200160208202803883390190505b5093506126c2898b8a6000815181106126b357fe5b6020026020010151898961294b565b9250826126d257506118ee915050565b6126df8a8a85898961294b565b9150816126ef57506118ee915050565b60005b885181101561280f5760005b88602001518110156127d1576127288a838151811061271957fe5b60200260200101518587612435565b945061273f89600001516127100161271087612435565b945060006127508d8d888c8c61294b565b90508061275d57506127d1565b8094508a838151811061276c57fe5b602002602001015185106127c85760008b848151811061278857fe5b60200260200101518c858151811061279c57fe5b6020026020010151870361271002816127b157fe5b0490508a6000015181116127c65750506127d1565b505b506001016126fe565b506127f08982815181106127e157fe5b60200260200101518486612435565b8582815181106127fc57fe5b60209081029190910101526001016126f2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561285b5761285b6128566004855185601401612c00565b612ca5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600061250982612cad565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000828211156128b6576128b661285660028585612d7e565b50900390565b6000826128cb57506000612509565b828202828482816128d857fe5b0414611e1357611e1361285660018686612d7e565b600082820183811015611e1357611e1361285660008686612d7e565b60008161291f5761291f61285660038585612d7e565b600082848161292a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061298057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e253000000000000000000000000000000000000000000000000000000001415612a90576040517f4cb8e2530000000000000000000000000000000000000000000000000000000090612a0c908a908a908590602401613881565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612b49565b6040517f9f76ad350000000000000000000000000000000000000000000000000000000090612ac99086908b908b908690602401613809565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff1684604051612b7291906137a5565b600060405180830381855afa9150503d8060008114612bad576040519150601f19603f3d011682016040523d82523d6000602084013e612bb2565b606091505b509150915081612bc957600094505050505061067f565b80806020019051612bdd91908101906135de565b600081518110612be957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b848484604051602401612c1f939291906139d4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051612d1191906137a5565b600060405180830381855afa9150503d8060008114612d4c576040519150601f19603f3d011682016040523d82523d6000602084013e612d51565b606091505b5091509150818015612d64575080516020145b15612d7757612d74816000612d9d565b92505b5050919050565b606063e946c1bb60e01b848484604051602401612c1f939291906139b2565b6000611e13838360008160200183511015612dc557612dc56128566005855185602001612c00565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b803561250981613c95565b600082601f830112612e0b578081fd5b8135612e1e612e1982613c45565b613c1e565b8181529150602080830190840160005b83811015612e5b57612e468760208435890101612ece565b83526020928301929190910190600101612e2e565b5050505092915050565b600082601f830112612e75578081fd5b8135612e83612e1982613c45565b818152915060208083019084810181840286018201871015612ea457600080fd5b60005b84811015612ec357813584529282019290820190600101612ea7565b505050505092915050565b600082601f830112612ede578081fd5b813567ffffffffffffffff811115612ef4578182fd5b612f2560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c1e565b9150808252836020828501011115612f3c57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b811461250957600080fd5b600060408284031215612f78578081fd5b612f826040613c1e565b9050813581526020820135602082015292915050565b60006101c0808385031215612fab578182fd5b612fb481613c1e565b915050612fc18383612df0565b8152612fd08360208401612df0565b6020820152612fe28360408401612df0565b6040820152612ff48360608401612df0565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561305657600080fd5b61306286838701612ece565b8385015261016092508285013591508082111561307e57600080fd5b61308a86838701612ece565b838501526101809250828501359150808211156130a657600080fd5b6130b286838701612ece565b838501526101a09250828501359150808211156130ce57600080fd5b506130db85828601612ece565b82840152505092915050565b6000602082840312156130f8578081fd5b8151611e1381613c95565b60008060408385031215613115578081fd5b825161312081613c95565b6020939093015192949293505050565b600080600060608486031215613144578081fd5b833561314f81613c95565b9250602084013561315f81613c95565b9150604084013561316f81613c95565b809150509250925092565b600080600080600060a08688031215613191578283fd5b853561319c81613c95565b945060208601356131ac81613c95565b935060408601356131bc81613c95565b925060608601356131cc81613c95565b9150608086013567ffffffffffffffff8111156131e7578182fd5b6131f388828901612e65565b9150509295509295909350565b60008060008060808587031215613215578182fd5b843561322081613c95565b9350602085013561323081613c95565b9250604085013561324081613c95565b9150606085013567ffffffffffffffff81111561325b578182fd5b61326787828801612e65565b91505092959194509250565b600080600080600060c0868803121561328a578283fd5b853561329581613c95565b945060208601356132a581613c95565b935060408601356132b581613c95565b9250606086013567ffffffffffffffff8111156132d0578182fd5b6132dc88828901612e65565b9250506132ec8760808801612f67565b90509295509295909350565b60008060006060848603121561330c578081fd5b833561331781613c95565b9250602084013561332781613c95565b9150604084013567ffffffffffffffff811115613342578182fd5b61334e86828701612e65565b9150509250925092565b60008060008060a0858703121561336d578182fd5b843561337881613c95565b9350602085013561338881613c95565b9250604085013567ffffffffffffffff8111156133a3578283fd5b6133af87828801612e65565b9250506133bf8660608701612f67565b905092959194509250565b600080600080608085870312156133df578182fd5b84356133ea81613c95565b93506133f98660208701612f55565b92506132408660408701612f55565b6000806040838503121561341a578182fd5b823567ffffffffffffffff80821115613431578384fd5b81850186601f820112613442578485fd5b80359250613452612e1984613c45565b80848252602080830192508084018a828389028701011115613472578889fd5b8894505b8685101561349d57803561348981613c95565b845260019490940193928101928101613476565b5090965087013593505050808211156134b4578283fd5b506134c185828601612e65565b9150509250929050565b600080602083850312156134dd578182fd5b823567ffffffffffffffff808211156134f4578384fd5b81850186601f820112613505578485fd5b8035925081831115613515578485fd5b8660208085028301011115613528578485fd5b60200196919550909350505050565b60008060408385031215613549578182fd5b823567ffffffffffffffff80821115613560578384fd5b81850186601f820112613571578485fd5b80359250613581612e1984613c45565b83815260208082019190838101885b878110156135b9576135a78c848435890101612f98565b85529382019390820190600101613590565b509197508801359450505050808211156135d1578283fd5b506134c185828601612dfb565b600060208083850312156135f0578182fd5b825167ffffffffffffffff811115613606578283fd5b80840185601f820112613617578384fd5b80519150613627612e1983613c45565b8281528381019082850185850284018601891015613643578687fd5b8693505b84841015613665578051835260019390930192918501918501613647565b50979650505050505050565b600080600083850360a0811215613686578182fd5b6060811215613693578182fd5b5061369e6060613c1e565b8451600781106136ac578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461316f578182fd5b6000602082840312156136ef578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015613741578151865260209586019590910190600101613723565b5093949350505050565b60008151808452613763816020860160208601613c65565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516137b7818460208701613c65565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152808516604084015250608060608301526118ee6080830184613710565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80861683528085166020840152506060604083015261067f6060830184613710565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015613992577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261398085835161374b565b94509285019290850190600101613946565b5092979650505050505050565b600060208252611e136020830184613710565b60608101600485106139c057fe5b938152602081019290925260409091015290565b60608101600885106139c057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252613a716040830185516136f6565b6020840151613a8360608401826136f6565b506040840151613a9660808401826136f6565b506060840151613aa960a08401826136f6565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152613b1a61020087018561374b565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152613b59828761374b565b838b015196508489820301868a0152613b72818861374b565b955050808a0151955050505080858303016101e086015250613b94818361374b565b8481036020860152613ba6818761374b565b979650505050505050565b90815260200190565b60006040820184835260406020840152808451808352606085019150602086019250835b81811015613c1257835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101613bde565b50909695505050505050565b60405181810167ffffffffffffffff81118282101715613c3d57600080fd5b604052919050565b600067ffffffffffffffff821115613c5b578081fd5b5060209081020190565b60005b83811015613c80578181015183820152602001613c68565b83811115613c8f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613cb757600080fd5b5056fea365627a7a723158204f89046f6ef91e066e56b4d991a1a9740ea0d2c2bfef6144bf56dbf1f65952d76c6578706572696d656e74616cf564736f6c63430005110040" }, "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b50600436106101365760003560e01c806368be3cf2116100b25780639f76ad3511610081578063c7f7142e11610066578063c7f7142e1461027b578063d0eea06d1461029b578063e68248f7146102ae57610136565b80639f76ad3514610255578063abffc7611461026857610136565b806368be3cf2146101fc5780636dd6b78d1461021c5780638b123a021461022f57806398cdafba1461024257610136565b80634cb8e2531161010957806359f515d0116100ee57806359f515d0146101c357806360ee052a146101d657806364ee6ade146101e957610136565b80634cb8e2531461019d57806358306ba0146101b057610136565b80631796fb871461013b5780632d753aa414610164578063354152a3146101775780634703a7e61461018a575b600080fd5b61014e6101493660046133ca565b6102c1565b60405161015b919061399f565b60405180910390f35b61014e61017236600461317a565b610492565b61014e6101853660046133ca565b610688565b61014e6101983660046132f8565b610842565b61014e6101ab3660046132f8565b610a1e565b61014e6101be366004613200565b610c38565b61014e6101d1366004613537565b610ccf565b61014e6101e43660046132f8565b610d78565b61014e6101f73660046132f8565b611038565b61020f61020a3660046134cb565b6111fe565b60405161015b9190613921565b61014e61022a3660046132f8565b61133d565b61014e61023d366004613537565b6115e5565b61014e610250366004613273565b6118be565b61014e610263366004613200565b6118f8565b61014e610276366004613408565b611afc565b61028e610289366004613130565b611cdf565b60405161015b91906137c1565b61014e6102a9366004613358565b611e1a565b61014e6102bc366004613408565b611e4b565b6060600082519050806040519080825280602002602001820160405280156102f3578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061035057fe5b602002602001015160405160240161036a939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516103f391906137a5565b6000604051808303818686fa925050503d806000811461042f576040519150601f19603f3d011682016040523d82523d6000602084013e610434565b606091505b5090925090506000821561045d578180602001905161045691908101906136de565b9050610465565b505050610488565b8086858151811061047257fe5b60209081029190910101525050506001016102f9565b5050949350505050565b6060600082519050806040519080825280602002602001820160405280156104c4578160200160208202803883390190505b50915073ffffffffffffffffffffffffffffffffffffffff87166104e8575061067f565b60005b8181101561067c57600060608973ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff16636e79e133905060e01b8b8b8b8b898151811061054357fe5b602002602001015160405160240161055e949392919061384a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e791906137a5565b6000604051808303818686fa925050503d8060008114610623576040519150601f19603f3d011682016040523d82523d6000602084013e610628565b606091505b50909250905060008215610651578180602001905161064a91908101906136de565b9050610659565b50505061067c565b8086858151811061066657fe5b60209081029190910101525050506001016104eb565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156106ba578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061071757fe5b6020026020010151604051602401610731939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107ba91906137a5565b6000604051808303818686fa925050503d80600081146107f6576040519150601f19603f3d011682016040523d82523d6000602084013e6107fb565b606091505b5090925090506000821561045d578180602001905161081d91908101906136de565b90508086858151811061082c57fe5b60209081029190910101525050506001016106c0565b606061084e838561201e565b8151604080518281526020808402820101909152818015610879578160200160208202803883390190505b50915060005b81811015610a155760006060610893612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a88815181106108dd57fe5b60200260200101516040516024016108f7939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098091906137a5565b6000604051808303818686fa925050503d80600081146109bc576040519150601f19603f3d011682016040523d82523d6000602084013e6109c1565b606091505b509092509050600082156109ea57818060200190516109e391908101906136de565b90506109f2565b505050610a15565b808685815181106109ff57fe5b602090810291909101015250505060010161087f565b50509392505050565b6060610a2a838561201e565b8151604080518281526020808402820101909152818015610a55578160200160208202803883390190505b5091506000610a626120a9565b9050600080805b84811015610c2c578373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480610ad657508373ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610b6e57610af98989898481518110610aec57fe5b60200260200101516120c1565b909350915073ffffffffffffffffffffffffffffffffffffffff82167331e085afd48a1d6e51cc193153d625e8f0514c7f1480610b5f575073ffffffffffffffffffffffffffffffffffffffff8216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b15610b6957600092505b610c0c565b610b7f8985898481518110610aec57fe5b90935091508215610c0c576000610b97858a866120c1565b909450905073ffffffffffffffffffffffffffffffffffffffff83167331e085afd48a1d6e51cc193153d625e8f0514c7f148015610c0057508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b15610c0a57600093505b505b82868281518110610c1957fe5b6020908102919091010152600101610a69565b50505050509392505050565b60608273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480610c9f57508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b15610ca957610cc7565b6060610cb6868585611038565b9050610cc3848683611038565b9150505b949350505050565b6060610cdb83836115e5565b905060005b8351811015610d7157818181518110610cf557fe5b6020026020010151600014610d6957610d50828281518110610d1357fe5b6020026020010151858381518110610d2757fe5b602002602001015160a00151868481518110610d3f57fe5b602002602001015160800151612435565b828281518110610d5c57fe5b6020026020010181815250505b600101610ce0565b5092915050565b6060610d84838561201e565b8151604080518281526020808402820101909152818015610daf578160200160208202803883390190505b5091506000610dbc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610dfc57610df786612477565b610dff565b60005b90506000610e0b6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610e4b57610e4686612477565b610e4e565b60005b905060005b8381101561102d576001610e656120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610efc578651610edb9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b602002602001015161250f565b878481518110610ee757fe5b60200260200101819350828152505050611019565b610f046120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610f6d578651610edb9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b8651600090610fa69085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b925090508015610ffc57610fdb857f2640f62c000000000000000000000000000000000000000000000000000000008361250f565b888581518110610fe757fe5b60200260200101819450828152505050611017565b600087848151811061100a57fe5b6020026020010181815250505b505b80611024575061102d565b50600101610e53565b505050509392505050565b6060611044838561201e565b815160408051828152602080840282010190915281801561106f578160200160208202803883390190505b50915060005b81811015610a155760006060611089612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a88815181106110d357fe5b60200260200101516040516024016110ed939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161117691906137a5565b6000604051808303818686fa925050503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b509092509050600082156109ea57818060200190516111d991908101906136de565b9050808685815181106111e857fe5b6020908102919091010152505050600101611075565b60408051828152602080840282010190915260609082801561123457816020015b606081526020019060019003908161121f5790505b50905060005b808314610d7157600060603086868581811061125257fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261128c57600080fd5b9091016020810191503567ffffffffffffffff8111156112ab57600080fd5b368190038213156112bb57600080fd5b6040516112c9929190613795565b600060405180830381855afa9150503d8060008114611304576040519150601f19603f3d011682016040523d82523d6000602084013e611309565b606091505b50915091508161131b57805160208201fd5b8084848151811061132857fe5b6020908102919091010152505060010161123a565b6060611349838561201e565b8151604080518281526020808402820101909152818015611374578160200160208202803883390190505b50915060006113816120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146113c1576113bc86612477565b6113c4565b60005b905060006113d06120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146114105761140b86612477565b611413565b60005b905060005b8381101561102d57600161142a6120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156114b45786516114939085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b87848151811061149f57fe5b602002602001018193508281525050506115d1565b6114bc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156115255786516114939084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b865160009061155e9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b9250905080156115b457611593847fcd7724c3000000000000000000000000000000000000000000000000000000008361250f565b88858151811061159f57fe5b602002602001018194508281525050506115cf565b60008784815181106115c257fe5b6020026020010181815250505b505b806115dc575061102d565b50600101611418565b60608251604051908082528060200260200182016040528015611612578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b845181146118b65783818151811061164857fe5b60200260200101515160001480611676575084818151811061166657fe5b6020026020010151608001516000145b80611698575084818151811061168857fe5b602002602001015160a001516000145b156116bc5760008382815181106116ab57fe5b6020026020010181815250506118ae565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061170857fe5b602002602001015189878151811061171c57fe5b6020026020010151604051602401611735929190613a5d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117be91906137a5565b6000604051808303818686fa925050503d80600081146117fa576040519150601f19603f3d011682016040523d82523d6000602084013e6117ff565b606091505b50915091508161182a57600085848151811061181757fe5b60200260200101818152505050506118ae565b611832612dce565b600080838060200190516118499190810190613671565b9194509250905060038351600681111561185f57fe5b14158061186a575080155b1561188e57600088878151811061187d57fe5b6020026020010181815250506118a8565b8188878151811061189b57fe5b6020026020010181815250505b50505050505b600101611634565b505092915050565b60606118ee858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b612657565b9695505050505050565b60606000825190508060405190808252806020026020018201604052801561192a578160200160208202803883390190505b509150600061193a878787611cdf565b905073ffffffffffffffffffffffffffffffffffffffff811661195f5750610cc79050565b60005b82811015611af157600060608373ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b88815181106119b957fe5b60200260200101516040516024016119d3939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611a5c91906137a5565b6000604051808303818686fa925050503d8060008114611a98576040519150601f19603f3d011682016040523d82523d6000602084013e611a9d565b606091505b50909250905060008215611ac65781806020019051611abf91908101906136de565b9050611ace565b505050611af1565b80878581518110611adb57fe5b6020908102919091010152505050600101611962565b505050949350505050565b606060008251905080604051908082528060200260200182016040528015611b2e578160200160208202803883390190505b50915060005b818110156118b65760006060611b4861281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff1663d06ca61f905060e01b888681518110611b9057fe5b60200260200101518a604051602401611baa929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c3391906137a5565b6000604051808303818686fa925050503d8060008114611c6f576040519150601f19603f3d011682016040523d82523d6000602084013e611c74565b606091505b50909250905060008215611cb45781806020019051611c9691908101906135de565b600189510381518110611ca557fe5b60200260200101519050611cbc565b5050506118b6565b80868581518110611cc957fe5b6020908102919091010152505050600101611b34565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611d1a90869086906024016137e2565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611da291906137a5565b600060405180830381855afa9150503d8060008114611ddd576040519150601f19603f3d011682016040523d82523d6000602084013e611de2565b606091505b5091509150818015611df5575080516020145b15611e0f57611e0581600c612835565b9350505050611e13565b5050505b9392505050565b606061067f858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000612657565b606060008251905080604051908082528060200260200182016040528015611e7d578160200160208202803883390190505b50915060005b818110156118b65760006060611e9761281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff16631f00ca74905060e01b888681518110611edf57fe5b60200260200101518a604051602401611ef9929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611f8291906137a5565b6000604051808303818686fa925050503d8060008114611fbe576040519150601f19603f3d011682016040523d82523d6000602084013e611fc3565b606091505b50909250905060008215611cb45781806020019051611fe591908101906135de565b600081518110611ff157fe5b602002602001015190508086858151811061200857fe5b6020908102919091010152505050600101611e83565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561208d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161208490613a00565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006120ce6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614612106578561211c565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121286120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146121605785612176565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121838861287a565b60ff16905060006121938861287a565b60ff169050600060606121a4612885565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4f61ff8b00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff929092169162061a8091612224916137a5565b6000604051808303818686fa925050503d8060008114612260576040519150601f19603f3d011682016040523d82523d6000602084013e612265565b606091505b50915091508161228257506000965086955061242d945050505050565b60008180602001905161229891908101906130e7565b90508073ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff16630c235d96905060e01b89898e60006040516024016122ef94939291906138eb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161237891906137a5565b6000604051808303818686fa925050503d80600081146123b4576040519150601f19603f3d011682016040523d82523d6000602084013e6123b9565b606091505b50909350915060008084156123e557838060200190516123dc9190810190613103565b925090506123fb565b506000995089985061242d975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e8502028161241757fe5b048161241f57fe5b049a50985050505050505050505b935093915050565b6000610cc78361246b61244f82600163ffffffff61289d16565b61245f888763ffffffff6128bc16565b9063ffffffff6128ed16565b9063ffffffff61290916565b6000612481612933565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016124b991906137c1565b60206040518083038186803b1580156124d157600080fd5b505afa1580156124e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061250991908101906130e7565b92915050565b60008073ffffffffffffffffffffffffffffffffffffffff85166125325761242d565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016125619190613bb1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516125ea91906137a5565b6000604051808303818686fa925050503d8060008114612626576040519150601f19603f3d011682016040523d82523d6000602084013e61262b565b606091505b509092509050811561264e578080602001905161264b91908101906136de565b92505b50935093915050565b6060612663868861201e565b845161266e576118ee565b6000806000875160405190808252806020026020018201604052801561269e578160200160208202803883390190505b5093506126c2898b8a6000815181106126b357fe5b6020026020010151898961294b565b9250826126d257506118ee915050565b6126df8a8a85898961294b565b9150816126ef57506118ee915050565b60005b885181101561280f5760005b88602001518110156127d1576127288a838151811061271957fe5b60200260200101518587612435565b945061273f89600001516127100161271087612435565b945060006127508d8d888c8c61294b565b90508061275d57506127d1565b8094508a838151811061276c57fe5b602002602001015185106127c85760008b848151811061278857fe5b60200260200101518c858151811061279c57fe5b6020026020010151870361271002816127b157fe5b0490508a6000015181116127c65750506127d1565b505b506001016126fe565b506127f08982815181106127e157fe5b60200260200101518486612435565b8582815181106127fc57fe5b60209081029190910101526001016126f2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561285b5761285b6128566004855185601401612c00565b612ca5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600061250982612cad565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000828211156128b6576128b661285660028585612d7e565b50900390565b6000826128cb57506000612509565b828202828482816128d857fe5b0414611e1357611e1361285660018686612d7e565b600082820183811015611e1357611e1361285660008686612d7e565b60008161291f5761291f61285660038585612d7e565b600082848161292a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061298057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e253000000000000000000000000000000000000000000000000000000001415612a90576040517f4cb8e2530000000000000000000000000000000000000000000000000000000090612a0c908a908a908590602401613881565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612b49565b6040517f9f76ad350000000000000000000000000000000000000000000000000000000090612ac99086908b908b908690602401613809565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff1684604051612b7291906137a5565b600060405180830381855afa9150503d8060008114612bad576040519150601f19603f3d011682016040523d82523d6000602084013e612bb2565b606091505b509150915081612bc957600094505050505061067f565b80806020019051612bdd91908101906135de565b600081518110612be957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b848484604051602401612c1f939291906139d4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051612d1191906137a5565b600060405180830381855afa9150503d8060008114612d4c576040519150601f19603f3d011682016040523d82523d6000602084013e612d51565b606091505b5091509150818015612d64575080516020145b15612d7757612d74816000612d9d565b92505b5050919050565b606063e946c1bb60e01b848484604051602401612c1f939291906139b2565b6000611e13838360008160200183511015612dc557612dc56128566005855185602001612c00565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b803561250981613c95565b600082601f830112612e0b578081fd5b8135612e1e612e1982613c45565b613c1e565b8181529150602080830190840160005b83811015612e5b57612e468760208435890101612ece565b83526020928301929190910190600101612e2e565b5050505092915050565b600082601f830112612e75578081fd5b8135612e83612e1982613c45565b818152915060208083019084810181840286018201871015612ea457600080fd5b60005b84811015612ec357813584529282019290820190600101612ea7565b505050505092915050565b600082601f830112612ede578081fd5b813567ffffffffffffffff811115612ef4578182fd5b612f2560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c1e565b9150808252836020828501011115612f3c57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b811461250957600080fd5b600060408284031215612f78578081fd5b612f826040613c1e565b9050813581526020820135602082015292915050565b60006101c0808385031215612fab578182fd5b612fb481613c1e565b915050612fc18383612df0565b8152612fd08360208401612df0565b6020820152612fe28360408401612df0565b6040820152612ff48360608401612df0565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561305657600080fd5b61306286838701612ece565b8385015261016092508285013591508082111561307e57600080fd5b61308a86838701612ece565b838501526101809250828501359150808211156130a657600080fd5b6130b286838701612ece565b838501526101a09250828501359150808211156130ce57600080fd5b506130db85828601612ece565b82840152505092915050565b6000602082840312156130f8578081fd5b8151611e1381613c95565b60008060408385031215613115578081fd5b825161312081613c95565b6020939093015192949293505050565b600080600060608486031215613144578081fd5b833561314f81613c95565b9250602084013561315f81613c95565b9150604084013561316f81613c95565b809150509250925092565b600080600080600060a08688031215613191578283fd5b853561319c81613c95565b945060208601356131ac81613c95565b935060408601356131bc81613c95565b925060608601356131cc81613c95565b9150608086013567ffffffffffffffff8111156131e7578182fd5b6131f388828901612e65565b9150509295509295909350565b60008060008060808587031215613215578182fd5b843561322081613c95565b9350602085013561323081613c95565b9250604085013561324081613c95565b9150606085013567ffffffffffffffff81111561325b578182fd5b61326787828801612e65565b91505092959194509250565b600080600080600060c0868803121561328a578283fd5b853561329581613c95565b945060208601356132a581613c95565b935060408601356132b581613c95565b9250606086013567ffffffffffffffff8111156132d0578182fd5b6132dc88828901612e65565b9250506132ec8760808801612f67565b90509295509295909350565b60008060006060848603121561330c578081fd5b833561331781613c95565b9250602084013561332781613c95565b9150604084013567ffffffffffffffff811115613342578182fd5b61334e86828701612e65565b9150509250925092565b60008060008060a0858703121561336d578182fd5b843561337881613c95565b9350602085013561338881613c95565b9250604085013567ffffffffffffffff8111156133a3578283fd5b6133af87828801612e65565b9250506133bf8660608701612f67565b905092959194509250565b600080600080608085870312156133df578182fd5b84356133ea81613c95565b93506133f98660208701612f55565b92506132408660408701612f55565b6000806040838503121561341a578182fd5b823567ffffffffffffffff80821115613431578384fd5b81850186601f820112613442578485fd5b80359250613452612e1984613c45565b80848252602080830192508084018a828389028701011115613472578889fd5b8894505b8685101561349d57803561348981613c95565b845260019490940193928101928101613476565b5090965087013593505050808211156134b4578283fd5b506134c185828601612e65565b9150509250929050565b600080602083850312156134dd578182fd5b823567ffffffffffffffff808211156134f4578384fd5b81850186601f820112613505578485fd5b8035925081831115613515578485fd5b8660208085028301011115613528578485fd5b60200196919550909350505050565b60008060408385031215613549578182fd5b823567ffffffffffffffff80821115613560578384fd5b81850186601f820112613571578485fd5b80359250613581612e1984613c45565b83815260208082019190838101885b878110156135b9576135a78c848435890101612f98565b85529382019390820190600101613590565b509197508801359450505050808211156135d1578283fd5b506134c185828601612dfb565b600060208083850312156135f0578182fd5b825167ffffffffffffffff811115613606578283fd5b80840185601f820112613617578384fd5b80519150613627612e1983613c45565b8281528381019082850185850284018601891015613643578687fd5b8693505b84841015613665578051835260019390930192918501918501613647565b50979650505050505050565b600080600083850360a0811215613686578182fd5b6060811215613693578182fd5b5061369e6060613c1e565b8451600781106136ac578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461316f578182fd5b6000602082840312156136ef578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015613741578151865260209586019590910190600101613723565b5093949350505050565b60008151808452613763816020860160208601613c65565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516137b7818460208701613c65565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152808516604084015250608060608301526118ee6080830184613710565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80861683528085166020840152506060604083015261067f6060830184613710565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015613992577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261398085835161374b565b94509285019290850190600101613946565b5092979650505050505050565b600060208252611e136020830184613710565b60608101600485106139c057fe5b938152602081019290925260409091015290565b60608101600885106139c057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252613a716040830185516136f6565b6020840151613a8360608401826136f6565b506040840151613a9660808401826136f6565b506060840151613aa960a08401826136f6565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152613b1a61020087018561374b565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152613b59828761374b565b838b015196508489820301868a0152613b72818861374b565b955050808a0151955050505080858303016101e086015250613b94818361374b565b8481036020860152613ba6818761374b565b979650505050505050565b90815260200190565b60006040820184835260406020840152808451808352606085019150602086019250835b81811015613c1257835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101613bde565b50909695505050505050565b60405181810167ffffffffffffffff81118282101715613c3d57600080fd5b604052919050565b600067ffffffffffffffff821115613c5b578081fd5b5060209081020190565b60005b83811015613c80578181015183820152602001613c68565b83811115613c8f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613cb757600080fd5b5056fea365627a7a72315820a87fc7253056a246e4a91d483bd03f17a76820224b3bf36e5df9de67803770596c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x608060405234801561001057600080fd5b50600436106101365760003560e01c806368be3cf2116100b25780639f76ad3511610081578063c7f7142e11610066578063c7f7142e1461027b578063d0eea06d1461029b578063e68248f7146102ae57610136565b80639f76ad3514610255578063abffc7611461026857610136565b806368be3cf2146101fc5780636dd6b78d1461021c5780638b123a021461022f57806398cdafba1461024257610136565b80634cb8e2531161010957806359f515d0116100ee57806359f515d0146101c357806360ee052a146101d657806364ee6ade146101e957610136565b80634cb8e2531461019d57806358306ba0146101b057610136565b80631796fb871461013b5780632d753aa414610164578063354152a3146101775780634703a7e61461018a575b600080fd5b61014e6101493660046133ca565b6102c1565b60405161015b919061399f565b60405180910390f35b61014e61017236600461317a565b610492565b61014e6101853660046133ca565b610688565b61014e6101983660046132f8565b610842565b61014e6101ab3660046132f8565b610a1e565b61014e6101be366004613200565b610c38565b61014e6101d1366004613537565b610ccf565b61014e6101e43660046132f8565b610d78565b61014e6101f73660046132f8565b611038565b61020f61020a3660046134cb565b6111fe565b60405161015b9190613921565b61014e61022a3660046132f8565b61133d565b61014e61023d366004613537565b6115e5565b61014e610250366004613273565b6118be565b61014e610263366004613200565b6118f8565b61014e610276366004613408565b611afc565b61028e610289366004613130565b611cdf565b60405161015b91906137c1565b61014e6102a9366004613358565b611e1a565b61014e6102bc366004613408565b611e4b565b6060600082519050806040519080825280602002602001820160405280156102f3578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061035057fe5b602002602001015160405160240161036a939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516103f391906137a5565b6000604051808303818686fa925050503d806000811461042f576040519150601f19603f3d011682016040523d82523d6000602084013e610434565b606091505b5090925090506000821561045d578180602001905161045691908101906136de565b9050610465565b505050610488565b8086858151811061047257fe5b60209081029190910101525050506001016102f9565b5050949350505050565b6060600082519050806040519080825280602002602001820160405280156104c4578160200160208202803883390190505b50915073ffffffffffffffffffffffffffffffffffffffff87166104e8575061067f565b60005b8181101561067c57600060608973ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff16636e79e133905060e01b8b8b8b8b898151811061054357fe5b602002602001015160405160240161055e949392919061384a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e791906137a5565b6000604051808303818686fa925050503d8060008114610623576040519150601f19603f3d011682016040523d82523d6000602084013e610628565b606091505b50909250905060008215610651578180602001905161064a91908101906136de565b9050610659565b50505061067c565b8086858151811061066657fe5b60209081029190910101525050506001016104eb565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156106ba578160200160208202803883390190505b50915060005b8181101561048857600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061071757fe5b6020026020010151604051602401610731939291906139e2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107ba91906137a5565b6000604051808303818686fa925050503d80600081146107f6576040519150601f19603f3d011682016040523d82523d6000602084013e6107fb565b606091505b5090925090506000821561045d578180602001905161081d91908101906136de565b90508086858151811061082c57fe5b60209081029190910101525050506001016106c0565b606061084e838561201e565b8151604080518281526020808402820101909152818015610879578160200160208202803883390190505b50915060005b81811015610a155760006060610893612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a88815181106108dd57fe5b60200260200101516040516024016108f7939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098091906137a5565b6000604051808303818686fa925050503d80600081146109bc576040519150601f19603f3d011682016040523d82523d6000602084013e6109c1565b606091505b509092509050600082156109ea57818060200190516109e391908101906136de565b90506109f2565b505050610a15565b808685815181106109ff57fe5b602090810291909101015250505060010161087f565b50509392505050565b6060610a2a838561201e565b8151604080518281526020808402820101909152818015610a55578160200160208202803883390190505b5091506000610a626120a9565b9050600080805b84811015610c2c578373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480610ad657508373ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610b6e57610af98989898481518110610aec57fe5b60200260200101516120c1565b909350915073ffffffffffffffffffffffffffffffffffffffff82167331e085afd48a1d6e51cc193153d625e8f0514c7f1480610b5f575073ffffffffffffffffffffffffffffffffffffffff8216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b15610b6957600092505b610c0c565b610b7f8985898481518110610aec57fe5b90935091508215610c0c576000610b97858a866120c1565b909450905073ffffffffffffffffffffffffffffffffffffffff83167331e085afd48a1d6e51cc193153d625e8f0514c7f148015610c0057508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b15610c0a57600093505b505b82868281518110610c1957fe5b6020908102919091010152600101610a69565b50505050509392505050565b60608273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480610c9f57508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b15610ca957610cc7565b6060610cb6868585611038565b9050610cc3848683611038565b9150505b949350505050565b6060610cdb83836115e5565b905060005b8351811015610d7157818181518110610cf557fe5b6020026020010151600014610d6957610d50828281518110610d1357fe5b6020026020010151858381518110610d2757fe5b602002602001015160a00151868481518110610d3f57fe5b602002602001015160800151612435565b828281518110610d5c57fe5b6020026020010181815250505b600101610ce0565b5092915050565b6060610d84838561201e565b8151604080518281526020808402820101909152818015610daf578160200160208202803883390190505b5091506000610dbc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610dfc57610df786612477565b610dff565b60005b90506000610e0b6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610e4b57610e4686612477565b610e4e565b60005b905060005b8381101561102d576001610e656120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610efc578651610edb9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b602002602001015161250f565b878481518110610ee757fe5b60200260200101819350828152505050611019565b610f046120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610f6d578651610edb9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b8651600090610fa69085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b925090508015610ffc57610fdb857f2640f62c000000000000000000000000000000000000000000000000000000008361250f565b888581518110610fe757fe5b60200260200101819450828152505050611017565b600087848151811061100a57fe5b6020026020010181815250505b505b80611024575061102d565b50600101610e53565b505050509392505050565b6060611044838561201e565b815160408051828152602080840282010190915281801561106f578160200160208202803883390190505b50915060005b81811015610a155760006060611089612091565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a88815181106110d357fe5b60200260200101516040516024016110ed939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161117691906137a5565b6000604051808303818686fa925050503d80600081146111b2576040519150601f19603f3d011682016040523d82523d6000602084013e6111b7565b606091505b509092509050600082156109ea57818060200190516111d991908101906136de565b9050808685815181106111e857fe5b6020908102919091010152505050600101611075565b60408051828152602080840282010190915260609082801561123457816020015b606081526020019060019003908161121f5790505b50905060005b808314610d7157600060603086868581811061125257fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261128c57600080fd5b9091016020810191503567ffffffffffffffff8111156112ab57600080fd5b368190038213156112bb57600080fd5b6040516112c9929190613795565b600060405180830381855afa9150503d8060008114611304576040519150601f19603f3d011682016040523d82523d6000602084013e611309565b606091505b50915091508161131b57805160208201fd5b8084848151811061132857fe5b6020908102919091010152505060010161123a565b6060611349838561201e565b8151604080518281526020808402820101909152818015611374578160200160208202803883390190505b50915060006113816120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146113c1576113bc86612477565b6113c4565b60005b905060006113d06120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146114105761140b86612477565b611413565b60005b905060005b8381101561102d57600161142a6120a9565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156114b45786516114939085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b87848151811061149f57fe5b602002602001018193508281525050506115d1565b6114bc6120a9565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156115255786516114939084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610ece57fe5b865160009061155e9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610ece57fe5b9250905080156115b457611593847fcd7724c3000000000000000000000000000000000000000000000000000000008361250f565b88858151811061159f57fe5b602002602001018194508281525050506115cf565b60008784815181106115c257fe5b6020026020010181815250505b505b806115dc575061102d565b50600101611418565b60608251604051908082528060200260200182016040528015611612578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b845181146118b65783818151811061164857fe5b60200260200101515160001480611676575084818151811061166657fe5b6020026020010151608001516000145b80611698575084818151811061168857fe5b602002602001015160a001516000145b156116bc5760008382815181106116ab57fe5b6020026020010181815250506118ae565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061170857fe5b602002602001015189878151811061171c57fe5b6020026020010151604051602401611735929190613a5d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117be91906137a5565b6000604051808303818686fa925050503d80600081146117fa576040519150601f19603f3d011682016040523d82523d6000602084013e6117ff565b606091505b50915091508161182a57600085848151811061181757fe5b60200260200101818152505050506118ae565b611832612dce565b600080838060200190516118499190810190613671565b9194509250905060038351600681111561185f57fe5b14158061186a575080155b1561188e57600088878151811061187d57fe5b6020026020010181815250506118a8565b8188878151811061189b57fe5b6020026020010181815250505b50505050505b600101611634565b505092915050565b60606118ee858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b612657565b9695505050505050565b60606000825190508060405190808252806020026020018201604052801561192a578160200160208202803883390190505b509150600061193a878787611cdf565b905073ffffffffffffffffffffffffffffffffffffffff811661195f5750610cc79050565b60005b82811015611af157600060608373ffffffffffffffffffffffffffffffffffffffff1662061a80600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b88815181106119b957fe5b60200260200101516040516024016119d3939291906138ba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611a5c91906137a5565b6000604051808303818686fa925050503d8060008114611a98576040519150601f19603f3d011682016040523d82523d6000602084013e611a9d565b606091505b50909250905060008215611ac65781806020019051611abf91908101906136de565b9050611ace565b505050611af1565b80878581518110611adb57fe5b6020908102919091010152505050600101611962565b505050949350505050565b606060008251905080604051908082528060200260200182016040528015611b2e578160200160208202803883390190505b50915060005b818110156118b65760006060611b4861281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff1663d06ca61f905060e01b888681518110611b9057fe5b60200260200101518a604051602401611baa929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c3391906137a5565b6000604051808303818686fa925050503d8060008114611c6f576040519150601f19603f3d011682016040523d82523d6000602084013e611c74565b606091505b50909250905060008215611cb45781806020019051611c9691908101906135de565b600189510381518110611ca557fe5b60200260200101519050611cbc565b5050506118b6565b80868581518110611cc957fe5b6020908102919091010152505050600101611b34565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611d1a90869086906024016137e2565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611da291906137a5565b600060405180830381855afa9150503d8060008114611ddd576040519150601f19603f3d011682016040523d82523d6000602084013e611de2565b606091505b5091509150818015611df5575080516020145b15611e0f57611e0581600c612835565b9350505050611e13565b5050505b9392505050565b606061067f858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000612657565b606060008251905080604051908082528060200260200182016040528015611e7d578160200160208202803883390190505b50915060005b818110156118b65760006060611e9761281d565b73ffffffffffffffffffffffffffffffffffffffff16620249f0600073ffffffffffffffffffffffffffffffffffffffff16631f00ca74905060e01b888681518110611edf57fe5b60200260200101518a604051602401611ef9929190613bba565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611f8291906137a5565b6000604051808303818686fa925050503d8060008114611fbe576040519150601f19603f3d011682016040523d82523d6000602084013e611fc3565b606091505b50909250905060008215611cb45781806020019051611fe591908101906135de565b600081518110611ff157fe5b602002602001015190508086858151811061200857fe5b6020908102919091010152505050600101611e83565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561208d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161208490613a00565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006120ce6120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614612106578561211c565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121286120a9565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146121605785612176565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006121838861287a565b60ff16905060006121938861287a565b60ff169050600060606121a4612885565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4f61ff8b00000000000000000000000000000000000000000000000000000000179052905173ffffffffffffffffffffffffffffffffffffffff929092169162061a8091612224916137a5565b6000604051808303818686fa925050503d8060008114612260576040519150601f19603f3d011682016040523d82523d6000602084013e612265565b606091505b50915091508161228257506000965086955061242d945050505050565b60008180602001905161229891908101906130e7565b90508073ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff16630c235d96905060e01b89898e60006040516024016122ef94939291906138eb565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161237891906137a5565b6000604051808303818686fa925050503d80600081146123b4576040519150601f19603f3d011682016040523d82523d6000602084013e6123b9565b606091505b50909350915060008084156123e557838060200190516123dc9190810190613103565b925090506123fb565b506000995089985061242d975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e8502028161241757fe5b048161241f57fe5b049a50985050505050505050505b935093915050565b6000610cc78361246b61244f82600163ffffffff61289d16565b61245f888763ffffffff6128bc16565b9063ffffffff6128ed16565b9063ffffffff61290916565b6000612481612933565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016124b991906137c1565b60206040518083038186803b1580156124d157600080fd5b505afa1580156124e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061250991908101906130e7565b92915050565b60008073ffffffffffffffffffffffffffffffffffffffff85166125325761242d565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016125619190613bb1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516125ea91906137a5565b6000604051808303818686fa925050503d8060008114612626576040519150601f19603f3d011682016040523d82523d6000602084013e61262b565b606091505b509092509050811561264e578080602001905161264b91908101906136de565b92505b50935093915050565b6060612663868861201e565b845161266e576118ee565b6000806000875160405190808252806020026020018201604052801561269e578160200160208202803883390190505b5093506126c2898b8a6000815181106126b357fe5b6020026020010151898961294b565b9250826126d257506118ee915050565b6126df8a8a85898961294b565b9150816126ef57506118ee915050565b60005b885181101561280f5760005b88602001518110156127d1576127288a838151811061271957fe5b60200260200101518587612435565b945061273f89600001516127100161271087612435565b945060006127508d8d888c8c61294b565b90508061275d57506127d1565b8094508a838151811061276c57fe5b602002602001015185106127c85760008b848151811061278857fe5b60200260200101518c858151811061279c57fe5b6020026020010151870361271002816127b157fe5b0490508a6000015181116127c65750506127d1565b505b506001016126fe565b506127f08982815181106127e157fe5b60200260200101518486612435565b8582815181106127fc57fe5b60209081029190910101526001016126f2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561285b5761285b6128566004855185601401612c00565b612ca5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600061250982612cad565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000828211156128b6576128b661285660028585612d7e565b50900390565b6000826128cb57506000612509565b828202828482816128d857fe5b0414611e1357611e1361285660018686612d7e565b600082820183811015611e1357611e1361285660008686612d7e565b60008161291f5761291f61285660038585612d7e565b600082848161292a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061298057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e253000000000000000000000000000000000000000000000000000000001415612a90576040517f4cb8e2530000000000000000000000000000000000000000000000000000000090612a0c908a908a908590602401613881565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612b49565b6040517f9f76ad350000000000000000000000000000000000000000000000000000000090612ac99086908b908b908690602401613809565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff1684604051612b7291906137a5565b600060405180830381855afa9150503d8060008114612bad576040519150601f19603f3d011682016040523d82523d6000602084013e612bb2565b606091505b509150915081612bc957600094505050505061067f565b80806020019051612bdd91908101906135de565b600081518110612be957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b848484604051602401612c1f939291906139d4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051612d1191906137a5565b600060405180830381855afa9150503d8060008114612d4c576040519150601f19603f3d011682016040523d82523d6000602084013e612d51565b606091505b5091509150818015612d64575080516020145b15612d7757612d74816000612d9d565b92505b5050919050565b606063e946c1bb60e01b848484604051602401612c1f939291906139b2565b6000611e13838360008160200183511015612dc557612dc56128566005855185602001612c00565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b803561250981613c95565b600082601f830112612e0b578081fd5b8135612e1e612e1982613c45565b613c1e565b8181529150602080830190840160005b83811015612e5b57612e468760208435890101612ece565b83526020928301929190910190600101612e2e565b5050505092915050565b600082601f830112612e75578081fd5b8135612e83612e1982613c45565b818152915060208083019084810181840286018201871015612ea457600080fd5b60005b84811015612ec357813584529282019290820190600101612ea7565b505050505092915050565b600082601f830112612ede578081fd5b813567ffffffffffffffff811115612ef4578182fd5b612f2560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c1e565b9150808252836020828501011115612f3c57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b811461250957600080fd5b600060408284031215612f78578081fd5b612f826040613c1e565b9050813581526020820135602082015292915050565b60006101c0808385031215612fab578182fd5b612fb481613c1e565b915050612fc18383612df0565b8152612fd08360208401612df0565b6020820152612fe28360408401612df0565b6040820152612ff48360608401612df0565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561305657600080fd5b61306286838701612ece565b8385015261016092508285013591508082111561307e57600080fd5b61308a86838701612ece565b838501526101809250828501359150808211156130a657600080fd5b6130b286838701612ece565b838501526101a09250828501359150808211156130ce57600080fd5b506130db85828601612ece565b82840152505092915050565b6000602082840312156130f8578081fd5b8151611e1381613c95565b60008060408385031215613115578081fd5b825161312081613c95565b6020939093015192949293505050565b600080600060608486031215613144578081fd5b833561314f81613c95565b9250602084013561315f81613c95565b9150604084013561316f81613c95565b809150509250925092565b600080600080600060a08688031215613191578283fd5b853561319c81613c95565b945060208601356131ac81613c95565b935060408601356131bc81613c95565b925060608601356131cc81613c95565b9150608086013567ffffffffffffffff8111156131e7578182fd5b6131f388828901612e65565b9150509295509295909350565b60008060008060808587031215613215578182fd5b843561322081613c95565b9350602085013561323081613c95565b9250604085013561324081613c95565b9150606085013567ffffffffffffffff81111561325b578182fd5b61326787828801612e65565b91505092959194509250565b600080600080600060c0868803121561328a578283fd5b853561329581613c95565b945060208601356132a581613c95565b935060408601356132b581613c95565b9250606086013567ffffffffffffffff8111156132d0578182fd5b6132dc88828901612e65565b9250506132ec8760808801612f67565b90509295509295909350565b60008060006060848603121561330c578081fd5b833561331781613c95565b9250602084013561332781613c95565b9150604084013567ffffffffffffffff811115613342578182fd5b61334e86828701612e65565b9150509250925092565b60008060008060a0858703121561336d578182fd5b843561337881613c95565b9350602085013561338881613c95565b9250604085013567ffffffffffffffff8111156133a3578283fd5b6133af87828801612e65565b9250506133bf8660608701612f67565b905092959194509250565b600080600080608085870312156133df578182fd5b84356133ea81613c95565b93506133f98660208701612f55565b92506132408660408701612f55565b6000806040838503121561341a578182fd5b823567ffffffffffffffff80821115613431578384fd5b81850186601f820112613442578485fd5b80359250613452612e1984613c45565b80848252602080830192508084018a828389028701011115613472578889fd5b8894505b8685101561349d57803561348981613c95565b845260019490940193928101928101613476565b5090965087013593505050808211156134b4578283fd5b506134c185828601612e65565b9150509250929050565b600080602083850312156134dd578182fd5b823567ffffffffffffffff808211156134f4578384fd5b81850186601f820112613505578485fd5b8035925081831115613515578485fd5b8660208085028301011115613528578485fd5b60200196919550909350505050565b60008060408385031215613549578182fd5b823567ffffffffffffffff80821115613560578384fd5b81850186601f820112613571578485fd5b80359250613581612e1984613c45565b83815260208082019190838101885b878110156135b9576135a78c848435890101612f98565b85529382019390820190600101613590565b509197508801359450505050808211156135d1578283fd5b506134c185828601612dfb565b600060208083850312156135f0578182fd5b825167ffffffffffffffff811115613606578283fd5b80840185601f820112613617578384fd5b80519150613627612e1983613c45565b8281528381019082850185850284018601891015613643578687fd5b8693505b84841015613665578051835260019390930192918501918501613647565b50979650505050505050565b600080600083850360a0811215613686578182fd5b6060811215613693578182fd5b5061369e6060613c1e565b8451600781106136ac578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461316f578182fd5b6000602082840312156136ef578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015613741578151865260209586019590910190600101613723565b5093949350505050565b60008151808452613763816020860160208601613c65565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516137b7818460208701613c65565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152808516604084015250608060608301526118ee6080830184613710565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff80861683528085166020840152506060604083015261067f6060830184613710565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015613992577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261398085835161374b565b94509285019290850190600101613946565b5092979650505050505050565b600060208252611e136020830184613710565b60608101600485106139c057fe5b938152602081019290925260409091015290565b60608101600885106139c057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252613a716040830185516136f6565b6020840151613a8360608401826136f6565b506040840151613a9660808401826136f6565b506060840151613aa960a08401826136f6565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152613b1a61020087018561374b565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152613b59828761374b565b838b015196508489820301868a0152613b72818861374b565b955050808a0151955050505080858303016101e086015250613b94818361374b565b8481036020860152613ba6818761374b565b979650505050505050565b90815260200190565b60006040820184835260406020840152808451808352606085019150602086019250835b81811015613c1257835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101613bde565b50909695505050505050565b60405181810167ffffffffffffffff81118282101715613c3d57600080fd5b604052919050565b600067ffffffffffffffff821115613c5b578081fd5b5060209081020190565b60005b83811015613c80578181015183820152602001613c68565b83811115613c8f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114613cb757600080fd5b5056fea365627a7a723158204f89046f6ef91e066e56b4d991a1a9740ea0d2c2bfef6144bf56dbf1f65952d76c6578706572696d656e74616cf564736f6c63430005110040" } } }, diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index c9ff5d7b42..6b04bd8ce6 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -9,6 +9,10 @@ { "note": "Add affiliate fee transformer migration and flash wallet address", "pr": 2622 + }, + { + "note": "Add BalancerBridge to returned object in `migration.ts`", + "pr": 2613 } ] }, diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index c8bddc7f11..8c8cc26e2c 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -379,6 +379,7 @@ export async function runMigrationsAsync( maximumGasPrice: NULL_ADDRESS, dexForwarderBridge: NULL_ADDRESS, multiBridge: NULL_ADDRESS, + balancerBridge: NULL_ADDRESS, exchangeProxyGovernor: NULL_ADDRESS, exchangeProxy: exchangeProxy.address, exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, diff --git a/packages/monorepo-scripts/CHANGELOG.json b/packages/monorepo-scripts/CHANGELOG.json index b303935536..05a7bebc51 100644 --- a/packages/monorepo-scripts/CHANGELOG.json +++ b/packages/monorepo-scripts/CHANGELOG.json @@ -9,6 +9,10 @@ { "note": "Add `Set` to `EXTERNAL_TYPE_MAP`.", "pr": 2350 + }, + { + "note": "Add `TFillData` to `EXTERNAL_TYPE_MAP`", + "pr": 2613 } ] }, diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index a657c60693..0d0685b576 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -24,6 +24,8 @@ export const docGenConfigs: DocGenConfigs = { // HACK: Asset-swapper specifies marketSell and marketBuy quotes with a descriminant MarketOperation Type to ignore the error, linking Buy and Sell to MarketOperation Buy: true, Sell: true, + // HACK: Asset-swapper specifies TFillData as any type that extends FillData + TFillData: true, IterableIterator: true, Set: true, }, diff --git a/packages/tslint-config/CHANGELOG.json b/packages/tslint-config/CHANGELOG.json index b9842edbf7..cbcecdedcd 100644 --- a/packages/tslint-config/CHANGELOG.json +++ b/packages/tslint-config/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "4.1.0", + "changes": [ + { + "note": "Set `no-non-null-assertion` to false", + "pr": 2613 + } + ] + }, { "version": "4.0.0", "changes": [ diff --git a/packages/tslint-config/tslint.json b/packages/tslint-config/tslint.json index f90d31b49b..f761c04ba7 100644 --- a/packages/tslint-config/tslint.json +++ b/packages/tslint-config/tslint.json @@ -65,7 +65,7 @@ "no-lodash-isnull": true, "no-lodash-isundefined": true, "no-misused-new": true, - "no-non-null-assertion": true, + "no-non-null-assertion": false, "no-parameter-reassignment": true, "no-redundant-jsdoc": true, "no-return-await": true, diff --git a/packages/utils/src/configured_bignumber.ts b/packages/utils/src/configured_bignumber.ts index 42d3aa3785..2c3317108d 100644 --- a/packages/utils/src/configured_bignumber.ts +++ b/packages/utils/src/configured_bignumber.ts @@ -34,4 +34,9 @@ if (isNode) { }; } +// HACK: CLobber config and set to prevent imported packages from poisoning +// global BigNumber config +(orig => (BigNumber.config = (..._args: any[]) => orig({})))(BigNumber.config); +BigNumber.set = BigNumber.config; + export { BigNumber }; diff --git a/yarn.lock b/yarn.lock index 3ee1e2f369..32da34e8ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1047,6 +1047,16 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@balancer-labs/sor@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-0.3.0.tgz#c221225d9a3d1791ebfc3c566f7a76843bca98fa" + integrity sha512-QTVkeDmcGCaEgBhcVSu8c7cz6HA1ueWRbniuT+Yh0N/sqcZIcDMdoCFcpq66SD+hOxQ88RvzShmJ+P/3vKbXfg== + dependencies: + bignumber.js "^9.0.0" + ethers "^4.0.39" + isomorphic-fetch "^2.2.1" + typescript "^3.8.3" + "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -3906,7 +3916,7 @@ big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" -bignumber.js@*, bignumber.js@~9.0.0: +bignumber.js@*, bignumber.js@^9.0.0, bignumber.js@~9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" @@ -6283,6 +6293,19 @@ elliptic@6.3.3: hash.js "^1.0.0" inherits "^2.0.1" +elliptic@6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -7115,6 +7138,21 @@ ethers@4.0.0-beta.3: uuid "2.0.1" xmlhttprequest "1.8.0" +ethers@^4.0.39: + version "4.0.47" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.47.tgz#91b9cd80473b1136dd547095ff9171bd1fc68c85" + integrity sha512-hssRYhngV4hiDNeZmVU/k5/E8xmLG8UpcNUzg6mb7lqhgpFPH/t7nuv20RjRrEf0gblzvi2XwR5Te+V3ZFc9pQ== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.2" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -9771,7 +9809,7 @@ isobject@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" -isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1: +isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" dependencies: @@ -16802,6 +16840,11 @@ typescript@3.5.x: version "3.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" +typescript@^3.8.3: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== + typewise-core@^1.2, typewise-core@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195"