From 164a5d44d928f43e722bec30148e2bd6a6c6ef27 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Wed, 31 Mar 2021 18:49:44 -0400 Subject: [PATCH] bsc<->development rebase (#189) * FQT: Pack Protocol/source name into source ID (#162) * `@0x/contracts-zero-ex`: Encode protocol ID and source name in bridge source ID `@0x/asset-swapper`: Use new bridge source ID encoding. * fix linter issues * contracts cleanup (#164) * `@0x/contracts-zero-ex`: Add PancakeSwapFeature * `@0x/contracts-zero-ex`: Remove tokenspender/allowance target/greedy tokens stuff.' `@0x/contract-addresses`: Add BSC addresses. Remove exchangeProxyAllowanceTarget. `@0x/migrations`: Remove exchangeProxyAllowanceTarget. * Update contracts/zero-ex/contracts/src/features/IPancakeSwapFeature.sol Co-authored-by: mzhu25 * `@0x/contracts-zero-ex`: Add sushiswap support to PancakeSwap * `@0x/contract-artifacts`: Regenerate artifacts `@0x/contract-wrappers`: Regenerate wrappers * `@0x/contract-addresses`: Add BSC addresses Co-authored-by: mzhu25 Co-authored-by: mzhu25 * feat: Better chain support (#163) * feat: Better chain support * feat: better chain support refactor deployment constants (#166) * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * Rework WETH out, replacing with address(0) * wat * hack DeploymentConstants for now * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * remove duped network addresses * Rework the bridge source encoder * Use the constants NATIVE_FEE_TOKEN in EP consumer * `@0x/contract-addresses`: Fix WBNB address (#170) Co-authored-by: Lawrence Forman * multichain enable cakez vip (#171) * feat: Better chain support * feat: better chain support refactor deployment constants (#166) * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * Rework WETH out, replacing with address(0) * wat * hack DeploymentConstants for now * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * remove duped network addresses * `asset-swapper`: enable pancake VIP route generation Co-authored-by: Jacob Evans Co-authored-by: Lawrence Forman * `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values (#172) * `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values * `@0x/contracts-zero-ex`: I am a bad protocologist Co-authored-by: Lawrence Forman * feat: BSC Nerve + Dodo + Nerve + Ellipsis (#181) * feat: BSC Nerve + DODO v1 * CHANGELOGs * Remove extra balance fetch * Add Belt * Added Ellipsis * Update FQT address * `@0x/contracts-zero-ex`: Delete TokenSpenderFeature and get stuff compiling * `@0x/asset-swapper`: fix compilation * prettier * `@0x/asset-swapper`: Truncate LiquidityProvider source ID name * Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts Co-authored-by: Jacob Evans * Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts Co-authored-by: Jacob Evans * `@0x/contracts-zero-ex`: Fix BakerySwap on PackageSwapFeature (#190) * address review comments Co-authored-by: mzhu25 Co-authored-by: Jacob Evans Co-authored-by: Lawrence Forman --- contracts/zero-ex/CHANGELOG.json | 29 + contracts/zero-ex/contracts/src/IZeroEx.sol | 3 +- .../src/errors/LibSpenderRichErrors.sol | 47 -- .../src/external/AllowanceTarget.sol | 56 -- .../src/external/IAllowanceTarget.sol | 40 - .../src/features/LiquidityProviderFeature.sol | 5 +- .../src/features/MetaTransactionsFeature.sol | 5 +- .../src/features/MultiplexFeature.sol | 6 +- .../src/features/NativeOrdersFeature.sol | 8 +- .../src/features/PancakeSwapFeature.sol | 423 +++++++++++ .../src/features/TokenSpenderFeature.sol | 137 ---- .../src/features/TransformERC20Feature.sol | 5 +- .../contracts/src/features/UniswapFeature.sol | 91 +-- .../interfaces/IPancakeSwapFeature.sol | 50 ++ .../NativeOrdersCancellation.sol | 5 +- .../native_orders/NativeOrdersInfo.sol | 4 +- .../native_orders/NativeOrdersSettlement.sol | 5 +- .../src/fixins/FixinTokenSpender.sol | 113 +-- .../src/migrations/FullMigration.sol | 25 +- .../src/storage/LibTokenSpenderStorage.sol | 46 -- .../transformers/bridges/BridgeAdapter.sol | 55 +- .../transformers/bridges/BridgeProtocols.sol | 46 ++ .../src/transformers/bridges/BridgeSource.sol | 47 -- .../transformers/bridges/IBridgeAdapter.sol | 12 +- .../bridges/mixins/MixinNerve.sol | 72 ++ .../bridges/mixins/MixinSushiswap.sol | 75 -- .../contracts/test/TestFixinTokenSpender.sol | 30 +- ...estMetaTransactionsNativeOrdersFeature.sol | 3 +- ...tMetaTransactionsTransformERC20Feature.sol | 2 +- .../test/TestNativeOrdersFeature.sol | 6 +- .../contracts/test/TestTokenSpender.sol | 31 - .../contracts/test/TestTransformERC20.sol | 2 +- contracts/zero-ex/package.json | 4 +- contracts/zero-ex/src/artifacts.ts | 4 - contracts/zero-ex/src/index.ts | 1 - contracts/zero-ex/src/migration.ts | 18 - contracts/zero-ex/src/wrappers.ts | 2 - .../zero-ex/test/allowance_target_test.ts | 82 --- contracts/zero-ex/test/artifacts.ts | 24 +- .../test/features/liquidity_provider_test.ts | 2 - .../zero-ex/test/features/multiplex_test.ts | 19 +- .../test/features/token_spender_test.ts | 137 ---- .../zero-ex/test/fixin_token_spender_test.ts | 126 +--- contracts/zero-ex/test/full_migration_test.ts | 27 - .../fill_quote_transformer_test.ts | 2 +- contracts/zero-ex/test/wrappers.ts | 12 +- contracts/zero-ex/tsconfig.json | 14 +- packages/asset-swapper/CHANGELOG.json | 16 + .../contracts/src/BalancerSampler.sol | 8 + .../contracts/src/BancorSampler.sol | 100 ++- .../contracts/src/CurveSampler.sol | 12 +- .../contracts/src/DODOSampler.sol | 38 +- .../contracts/src/DODOV2Sampler.sol | 8 +- .../contracts/src/DeploymentConstants.sol | 353 --------- .../contracts/src/ERC20BridgeSampler.sol | 2 - .../contracts/src/Eth2DaiSampler.sol | 18 +- .../contracts/src/KyberSampler.sol | 85 ++- .../src/LiquidityProviderSampler.sol | 4 + .../contracts/src/MStableSampler.sol | 26 +- .../contracts/src/MooniswapSampler.sol | 26 +- .../contracts/src/ShellSampler.sol | 12 +- .../contracts/src/SushiSwapSampler.sol | 96 --- .../contracts/src/UniswapSampler.sol | 52 +- .../contracts/src/UniswapV2Sampler.sol | 12 +- .../contracts/test/TestERC20BridgeSampler.sol | 93 +-- packages/asset-swapper/package.json | 2 +- packages/asset-swapper/src/constants.ts | 8 +- packages/asset-swapper/src/index.ts | 9 +- .../exchange_proxy_swap_quote_consumer.ts | 76 +- .../quote_consumers/quote_consumer_utils.ts | 4 +- packages/asset-swapper/src/swap_quoter.ts | 1 + .../bridge_source_utils.ts | 145 +++- .../utils/market_operation_utils/constants.ts | 606 +++++++++++---- .../src/utils/market_operation_utils/index.ts | 54 +- .../utils/market_operation_utils/orders.ts | 104 +-- .../utils/market_operation_utils/sampler.ts | 3 + .../sampler_operations.ts | 692 ++++++++---------- .../market_operation_utils/shell_utils.ts | 8 - .../src/utils/market_operation_utils/types.ts | 39 +- packages/asset-swapper/test/artifacts.ts | 4 - .../contracts/bridge_sampler_mainnet_test.ts | 25 +- .../contracts/erc20_bridge_sampler_test.ts | 158 ++-- .../asset-swapper/test/dex_sampler_test.ts | 118 ++- .../test/market_operation_utils_test.ts | 17 +- .../test/utils/mock_sampler_contract.ts | 43 +- .../asset-swapper/test/utils/test_helpers.ts | 2 +- packages/asset-swapper/test/wrappers.ts | 2 - packages/asset-swapper/tsconfig.json | 2 - packages/contract-addresses/CHANGELOG.json | 17 + packages/contract-addresses/addresses.json | 56 +- packages/contract-addresses/src/index.ts | 2 +- packages/contract-artifacts/CHANGELOG.json | 9 + .../contract-artifacts/artifacts/IZeroEx.json | 69 +- packages/contract-wrappers/CHANGELOG.json | 9 + .../src/generated-wrappers/i_zero_ex.ts | 314 +++----- packages/migrations/CHANGELOG.json | 8 + packages/migrations/src/migration.ts | 2 - packages/protocol-utils/CHANGELOG.json | 9 + .../protocol-utils/src/transformer_utils.ts | 52 +- 99 files changed, 2559 insertions(+), 3029 deletions(-) delete mode 100644 contracts/zero-ex/contracts/src/errors/LibSpenderRichErrors.sol delete mode 100644 contracts/zero-ex/contracts/src/external/AllowanceTarget.sol delete mode 100644 contracts/zero-ex/contracts/src/external/IAllowanceTarget.sol create mode 100644 contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol delete mode 100644 contracts/zero-ex/contracts/src/features/TokenSpenderFeature.sol create mode 100644 contracts/zero-ex/contracts/src/features/interfaces/IPancakeSwapFeature.sol delete mode 100644 contracts/zero-ex/contracts/src/storage/LibTokenSpenderStorage.sol create mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol delete mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/BridgeSource.sol create mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinNerve.sol delete mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinSushiswap.sol delete mode 100644 contracts/zero-ex/contracts/test/TestTokenSpender.sol delete mode 100644 contracts/zero-ex/test/allowance_target_test.ts delete mode 100644 contracts/zero-ex/test/features/token_spender_test.ts delete mode 100644 packages/asset-swapper/contracts/src/DeploymentConstants.sol delete mode 100644 packages/asset-swapper/contracts/src/SushiSwapSampler.sol delete mode 100644 packages/asset-swapper/src/utils/market_operation_utils/shell_utils.ts diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index ff39578832..d6234c2c74 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -1,4 +1,33 @@ [ + { + "version": "0.21.0", + "changes": [ + { + "note": "Encoding protocol ID and source name in bridge source ID", + "pr": 162 + }, + { + "note": "Add PancakeSwapFeature", + "pr": 164 + }, + { + "note": "Remove TokenSpender/AllowanceTarget/greedy tokens stuff", + "pr": 164 + }, + { + "note": "Added Nerve in BridgeAdapter", + "pr": 181 + }, + { + "note": "Delete TokenSpenderFeature", + "pr": 189 + }, + { + "note": "Fix PancakeSwapFeature BakerySwap swap selector", + "pr": 190 + } + ] + }, { "version": "0.20.0", "changes": [ diff --git a/contracts/zero-ex/contracts/src/IZeroEx.sol b/contracts/zero-ex/contracts/src/IZeroEx.sol index 7421d070dd..efb92fe482 100644 --- a/contracts/zero-ex/contracts/src/IZeroEx.sol +++ b/contracts/zero-ex/contracts/src/IZeroEx.sol @@ -26,6 +26,7 @@ import "./features/interfaces/ITokenSpenderFeature.sol"; import "./features/interfaces/ITransformERC20Feature.sol"; import "./features/interfaces/IMetaTransactionsFeature.sol"; import "./features/interfaces/IUniswapFeature.sol"; +import "./features/interfaces/IPancakeSwapFeature.sol"; import "./features/interfaces/ILiquidityProviderFeature.sol"; import "./features/interfaces/INativeOrdersFeature.sol"; import "./features/interfaces/IBatchFillNativeOrdersFeature.sol"; @@ -36,10 +37,10 @@ import "./features/interfaces/IMultiplexFeature.sol"; interface IZeroEx is IOwnableFeature, ISimpleFunctionRegistryFeature, - ITokenSpenderFeature, ITransformERC20Feature, IMetaTransactionsFeature, IUniswapFeature, + IPancakeSwapFeature, ILiquidityProviderFeature, INativeOrdersFeature, IBatchFillNativeOrdersFeature, diff --git a/contracts/zero-ex/contracts/src/errors/LibSpenderRichErrors.sol b/contracts/zero-ex/contracts/src/errors/LibSpenderRichErrors.sol deleted file mode 100644 index 1c72ad7624..0000000000 --- a/contracts/zero-ex/contracts/src/errors/LibSpenderRichErrors.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; - - -library LibSpenderRichErrors { - - // solhint-disable func-name-mixedcase - - function SpenderERC20TransferFromFailedError( - address token, - address owner, - address to, - uint256 amount, - bytes memory errorData - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - bytes4(keccak256("SpenderERC20TransferFromFailedError(address,address,address,uint256,bytes)")), - token, - owner, - to, - amount, - errorData - ); - } -} diff --git a/contracts/zero-ex/contracts/src/external/AllowanceTarget.sol b/contracts/zero-ex/contracts/src/external/AllowanceTarget.sol deleted file mode 100644 index 0efab020ab..0000000000 --- a/contracts/zero-ex/contracts/src/external/AllowanceTarget.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; -import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol"; -import "../errors/LibSpenderRichErrors.sol"; -import "./IAllowanceTarget.sol"; - - -/// @dev The allowance target for the TokenSpender feature. -contract AllowanceTarget is - IAllowanceTarget, - AuthorizableV06 -{ - // solhint-disable no-unused-vars,indent,no-empty-blocks - using LibRichErrorsV06 for bytes; - - /// @dev Execute an arbitrary call. Only an authority can call this. - /// @param target The call target. - /// @param callData The call data. - /// @return resultData The data returned by the call. - function executeCall( - address payable target, - bytes calldata callData - ) - external - override - onlyAuthorized - returns (bytes memory resultData) - { - bool success; - (success, resultData) = target.call(callData); - if (!success) { - resultData.rrevert(); - } - } -} diff --git a/contracts/zero-ex/contracts/src/external/IAllowanceTarget.sol b/contracts/zero-ex/contracts/src/external/IAllowanceTarget.sol deleted file mode 100644 index fd63541652..0000000000 --- a/contracts/zero-ex/contracts/src/external/IAllowanceTarget.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/interfaces/IAuthorizableV06.sol"; - - -/// @dev The allowance target for the TokenSpender feature. -interface IAllowanceTarget is - IAuthorizableV06 -{ - /// @dev Execute an arbitrary call. Only an authority can call this. - /// @param target The call target. - /// @param callData The call data. - /// @return resultData The data returned by the call. - function executeCall( - address payable target, - bytes calldata callData - ) - external - returns (bytes memory resultData); -} diff --git a/contracts/zero-ex/contracts/src/features/LiquidityProviderFeature.sol b/contracts/zero-ex/contracts/src/features/LiquidityProviderFeature.sol index 8d6ba51312..5a696b6b5c 100644 --- a/contracts/zero-ex/contracts/src/features/LiquidityProviderFeature.sol +++ b/contracts/zero-ex/contracts/src/features/LiquidityProviderFeature.sol @@ -47,15 +47,14 @@ contract LiquidityProviderFeature is /// @dev Name of this feature. string public constant override FEATURE_NAME = "LiquidityProviderFeature"; /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 3); + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 4); /// @dev The sandbox contract address. ILiquidityProviderSandbox public immutable sandbox; - constructor(LiquidityProviderSandbox sandbox_, bytes32 greedyTokensBloomFilter) + constructor(LiquidityProviderSandbox sandbox_) public FixinCommon() - FixinTokenSpender(greedyTokensBloomFilter) { sandbox = sandbox_; } diff --git a/contracts/zero-ex/contracts/src/features/MetaTransactionsFeature.sol b/contracts/zero-ex/contracts/src/features/MetaTransactionsFeature.sol index d57a8f88d7..07a0ca4bb7 100644 --- a/contracts/zero-ex/contracts/src/features/MetaTransactionsFeature.sol +++ b/contracts/zero-ex/contracts/src/features/MetaTransactionsFeature.sol @@ -78,7 +78,7 @@ contract MetaTransactionsFeature is /// @dev Name of this feature. string public constant override FEATURE_NAME = "MetaTransactions"; /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0); + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1); /// @dev EIP712 typehash of the `MetaTransactionData` struct. bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256( "MetaTransactionData(" @@ -105,11 +105,10 @@ contract MetaTransactionsFeature is } } - constructor(address zeroExAddress, bytes32 greedyTokensBloomFilter) + constructor(address zeroExAddress) public FixinCommon() FixinEIP712(zeroExAddress) - FixinTokenSpender(greedyTokensBloomFilter) { // solhint-disable-next-line no-empty-blocks } diff --git a/contracts/zero-ex/contracts/src/features/MultiplexFeature.sol b/contracts/zero-ex/contracts/src/features/MultiplexFeature.sol index e02df5f7ac..a4d90ae987 100644 --- a/contracts/zero-ex/contracts/src/features/MultiplexFeature.sol +++ b/contracts/zero-ex/contracts/src/features/MultiplexFeature.sol @@ -55,7 +55,7 @@ contract MultiplexFeature is /// @dev Name of this feature. string public constant override FEATURE_NAME = "MultiplexFeature"; /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1); /// @dev The WETH token contract. IEtherTokenV06 private immutable weth; @@ -73,12 +73,10 @@ contract MultiplexFeature is constructor( address zeroExAddress, IEtherTokenV06 weth_, - ILiquidityProviderSandbox sandbox_, - bytes32 greedyTokensBloomFilter + ILiquidityProviderSandbox sandbox_ ) public FixinEIP712(zeroExAddress) - FixinTokenSpender(greedyTokensBloomFilter) { weth = weth_; sandbox = sandbox_; diff --git a/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol b/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol index fd5ddfc715..93684b4dac 100644 --- a/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol +++ b/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol @@ -34,15 +34,14 @@ contract NativeOrdersFeature is /// @dev Name of this feature. string public constant override FEATURE_NAME = "LimitOrders"; /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1); + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0); constructor( address zeroExAddress, IEtherTokenV06 weth, IStaking staking, FeeCollectorController feeCollectorController, - uint32 protocolFeeMultiplier, - bytes32 greedyTokensBloomFilter + uint32 protocolFeeMultiplier ) public NativeOrdersSettlement( @@ -50,8 +49,7 @@ contract NativeOrdersFeature is weth, staking, feeCollectorController, - protocolFeeMultiplier, - greedyTokensBloomFilter + protocolFeeMultiplier ) { // solhint-disable no-empty-blocks diff --git a/contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol b/contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol new file mode 100644 index 0000000000..c38f5444ca --- /dev/null +++ b/contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2021 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.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "../migrations/LibMigrate.sol"; +import "../fixins/FixinCommon.sol"; +import "./interfaces/IFeature.sol"; +import "./interfaces/IPancakeSwapFeature.sol"; + + +/// @dev VIP pancake fill functions. +contract PancakeSwapFeature is + IFeature, + IPancakeSwapFeature, + FixinCommon +{ + /// @dev Name of this feature. + string public constant override FEATURE_NAME = "PancakeSwapFeature"; + /// @dev Version of this feature. + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1); + /// @dev WBNB contract. + IEtherTokenV06 private immutable WBNB; + + // 0xFF + address of the PancakeSwap factory contract. + uint256 constant private FF_PANCAKESWAP_FACTORY = 0xffbcfccbde45ce874adcb698cc183debcf179528120000000000000000000000; + // 0xFF + address of the BakerySwap factory contract. + uint256 constant private FF_BAKERYSWAP_FACTORY = 0xff01bf7c66c6bd861915cdaae475042d3c4bae16a70000000000000000000000; + // 0xFF + address of the SushiSwap factory contract. + uint256 constant private FF_SUSHISWAP_FACTORY = 0xffc35DADB65012eC5796536bD9864eD8773aBc74C40000000000000000000000; + // Init code hash of the PancakeSwap pair contract. + uint256 constant private PANCAKESWAP_PAIR_INIT_CODE_HASH = 0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66; + // Init code hash of the BakerySwap pair contract. + uint256 constant private BAKERYSWAP_PAIR_INIT_CODE_HASH = 0xe2e87433120e32c4738a7d8f3271f3d872cbe16241d67537139158d90bac61d3; + // Init code hash of the SushiSwap pair contract. + uint256 constant private SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303; + // Mask of the lower 20 bytes of a bytes32. + uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; + // BNB pseudo-token address. + uint256 constant private ETH_TOKEN_ADDRESS_32 = 0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee; + // Maximum token quantity that can be swapped against the PancakeSwapPair contract. + uint256 constant private MAX_SWAP_AMOUNT = 2**112; + + // bytes4(keccak256("executeCall(address,bytes)")) + uint256 constant private ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32 = 0xbca8c7b500000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("getReserves()")) + uint256 constant private PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32 = 0x0902f1ac00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("swap(uint256,uint256,address,bytes)")) + uint256 constant private PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x022c0d9f00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("swap(uint256,uint256,address)")) + uint256 constant private BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x6d9a640a00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("transferFrom(address,address,uint256)")) + uint256 constant private TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("allowance(address,address)")) + uint256 constant private ALLOWANCE_CALL_SELECTOR_32 = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("withdraw(uint256)")) + uint256 constant private WETH_WITHDRAW_CALL_SELECTOR_32 = 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("deposit()")) + uint256 constant private WETH_DEPOSIT_CALL_SELECTOR_32 = 0xd0e30db000000000000000000000000000000000000000000000000000000000; + // bytes4(keccak256("transfer(address,uint256)")) + uint256 constant private ERC20_TRANSFER_CALL_SELECTOR_32 = 0xa9059cbb00000000000000000000000000000000000000000000000000000000; + + /// @dev Construct this contract. + /// @param wbnb The WBNB contract. + constructor(IEtherTokenV06 wbnb) public { + WBNB = wbnb; + } + + /// @dev Initialize and register this feature. + /// Should be delegatecalled by `Migrate.migrate()`. + /// @return success `LibMigrate.SUCCESS` on success. + function migrate() + external + returns (bytes4 success) + { + _registerFeatureFunction(this.sellToPancakeSwap.selector); + return LibMigrate.MIGRATE_SUCCESS; + } + + /// @dev Efficiently sell directly to pancake/BakerySwap/SushiSwap. + /// @param tokens Sell path. + /// @param sellAmount of `tokens[0]` Amount to sell. + /// @param minBuyAmount Minimum amount of `tokens[-1]` to buy. + /// @param fork The protocol fork to use. + /// @return buyAmount Amount of `tokens[-1]` bought. + function sellToPancakeSwap( + IERC20TokenV06[] calldata tokens, + uint256 sellAmount, + uint256 minBuyAmount, + ProtocolFork fork + ) + external + payable + override + returns (uint256 buyAmount) + { + require(tokens.length > 1, "PancakeSwapFeature/InvalidTokensLength"); + { + // Load immutables onto the stack. + IEtherTokenV06 wbnb = WBNB; + + // Store some vars in memory to get around stack limits. + assembly { + // calldataload(mload(0xA00)) == first element of `tokens` array + mstore(0xA00, add(calldataload(0x04), 0x24)) + // mload(0xA20) == fork + mstore(0xA20, fork) + // mload(0xA40) == WBNB + mstore(0xA40, wbnb) + } + } + + assembly { + // numPairs == tokens.length - 1 + let numPairs := sub(calldataload(add(calldataload(0x04), 0x4)), 1) + // We use the previous buy amount as the sell amount for the next + // pair in a path. So for the first swap we want to set it to `sellAmount`. + buyAmount := sellAmount + let buyToken + let nextPair := 0 + + for {let i := 0} lt(i, numPairs) {i := add(i, 1)} { + // sellToken = tokens[i] + let sellToken := loadTokenAddress(i) + // buyToken = tokens[i+1] + buyToken := loadTokenAddress(add(i, 1)) + // The canonical ordering of this token pair. + let pairOrder := lt(normalizeToken(sellToken), normalizeToken(buyToken)) + + // Compute the pair address if it hasn't already been computed + // from the last iteration. + let pair := nextPair + if iszero(pair) { + pair := computePairAddress(sellToken, buyToken) + nextPair := 0 + } + + if iszero(i) { + // This is the first token in the path. + switch eq(sellToken, ETH_TOKEN_ADDRESS_32) + case 0 { // Not selling BNB. Selling an ERC20 instead. + // Make sure BNB was not attached to the call. + if gt(callvalue(), 0) { + revert(0, 0) + } + // For the first pair we need to transfer sellTokens into the + // pair contract. + moveTakerTokensTo(sellToken, pair, sellAmount) + } + default { + // If selling BNB, we need to wrap it to WBNB and transfer to the + // pair contract. + if iszero(eq(callvalue(), sellAmount)) { + revert(0, 0) + } + sellToken := mload(0xA40)// Re-assign to WBNB + // Call `WBNB.deposit{value: sellAmount}()` + mstore(0xB00, WETH_DEPOSIT_CALL_SELECTOR_32) + if iszero(call(gas(), sellToken, sellAmount, 0xB00, 0x4, 0x00, 0x0)) { + bubbleRevert() + } + // Call `WBNB.transfer(pair, sellAmount)` + mstore(0xB00, ERC20_TRANSFER_CALL_SELECTOR_32) + mstore(0xB04, pair) + mstore(0xB24, sellAmount) + if iszero(call(gas(), sellToken, 0, 0xB00, 0x44, 0x00, 0x0)) { + bubbleRevert() + } + } + // No need to check results, if deposit/transfers failed the PancakeSwapPair will + // reject our trade (or it may succeed if somehow the reserve was out of sync) + // this is fine for the taker. + } + + // Call pair.getReserves(), store the results at `0xC00` + mstore(0xB00, PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32) + if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) { + bubbleRevert() + } + // Revert if the pair contract does not return at least two words. + if lt(returndatasize(), 0x40) { + mstore(0, pair) + revert(0, 32) + } + + // Sell amount for this hop is the previous buy amount. + let pairSellAmount := buyAmount + // Compute the buy amount based on the pair reserves. + { + let sellReserve + let buyReserve + switch iszero(pairOrder) + case 0 { + // Transpose if pair order is different. + sellReserve := mload(0xC00) + buyReserve := mload(0xC20) + } + default { + sellReserve := mload(0xC20) + buyReserve := mload(0xC00) + } + // Ensure that the sellAmount is < 2¹¹². + if gt(pairSellAmount, MAX_SWAP_AMOUNT) { + revert(0, 0) + } + // Pairs are in the range (0, 2¹¹²) so this shouldn't overflow. + // buyAmount = (pairSellAmount * 997 * buyReserve) / + // (pairSellAmount * 997 + sellReserve * 1000); + let sellAmountWithFee := mul(pairSellAmount, 997) + buyAmount := div( + mul(sellAmountWithFee, buyReserve), + add(sellAmountWithFee, mul(sellReserve, 1000)) + ) + } + + let receiver + // Is this the last pair contract? + switch eq(add(i, 1), numPairs) + case 0 { + // Not the last pair contract, so forward bought tokens to + // the next pair contract. + nextPair := computePairAddress( + buyToken, + loadTokenAddress(add(i, 2)) + ) + receiver := nextPair + } + default { + // The last pair contract. + // Forward directly to taker UNLESS they want BNB back. + switch eq(buyToken, ETH_TOKEN_ADDRESS_32) + case 0 { + receiver := caller() + } + default { + receiver := address() + } + } + + // Call pair.swap() + switch mload(0xA20) // fork + case 1 { + mstore(0xB00, BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32) + } + default { + mstore(0xB00, PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32) + } + switch pairOrder + case 0 { + mstore(0xB04, buyAmount) + mstore(0xB24, 0) + } + default { + mstore(0xB04, 0) + mstore(0xB24, buyAmount) + } + mstore(0xB44, receiver) + mstore(0xB64, 0x80) + mstore(0xB84, 0) + if iszero(call(gas(), pair, 0, 0xB00, 0xA4, 0, 0)) { + bubbleRevert() + } + } // End for-loop. + + // If buying BNB, unwrap the WBNB first + if eq(buyToken, ETH_TOKEN_ADDRESS_32) { + // Call `WBNB.withdraw(buyAmount)` + mstore(0xB00, WETH_WITHDRAW_CALL_SELECTOR_32) + mstore(0xB04, buyAmount) + if iszero(call(gas(), mload(0xA40), 0, 0xB00, 0x24, 0x00, 0x0)) { + bubbleRevert() + } + // Transfer BNB to the caller. + if iszero(call(gas(), caller(), buyAmount, 0xB00, 0x0, 0x00, 0x0)) { + bubbleRevert() + } + } + + // Functions /////////////////////////////////////////////////////// + + // Load a token address from the `tokens` calldata argument. + function loadTokenAddress(idx) -> addr { + addr := and(ADDRESS_MASK, calldataload(add(mload(0xA00), mul(idx, 0x20)))) + } + + // Convert BNB pseudo-token addresses to WBNB. + function normalizeToken(token) -> normalized { + normalized := token + // Translate BNB pseudo-tokens to WBNB. + if eq(token, ETH_TOKEN_ADDRESS_32) { + normalized := mload(0xA40) + } + } + + // Compute the address of the PancakeSwapPair contract given two + // tokens. + function computePairAddress(tokenA, tokenB) -> pair { + // Convert BNB pseudo-token addresses to WBNB. + tokenA := normalizeToken(tokenA) + tokenB := normalizeToken(tokenB) + // There is one contract for every combination of tokens, + // which is deployed using CREATE2. + // The derivation of this address is given by: + // address(keccak256(abi.encodePacked( + // bytes(0xFF), + // address(PANCAKESWAP_FACTORY_ADDRESS), + // keccak256(abi.encodePacked( + // tokenA < tokenB ? tokenA : tokenB, + // tokenA < tokenB ? tokenB : tokenA, + // )), + // bytes32(PANCAKESWAP_PAIR_INIT_CODE_HASH), + // ))); + + // Compute the salt (the hash of the sorted tokens). + // Tokens are written in reverse memory order to packed encode + // them as two 20-byte values in a 40-byte chunk of memory + // starting at 0xB0C. + switch lt(tokenA, tokenB) + case 0 { + mstore(0xB14, tokenA) + mstore(0xB00, tokenB) + } + default { + mstore(0xB14, tokenB) + mstore(0xB00, tokenA) + } + let salt := keccak256(0xB0C, 0x28) + // Compute the pair address by hashing all the components together. + switch mload(0xA20) // fork + case 0 { + mstore(0xB00, FF_PANCAKESWAP_FACTORY) + mstore(0xB15, salt) + mstore(0xB35, PANCAKESWAP_PAIR_INIT_CODE_HASH) + } + case 1 { + mstore(0xB00, FF_BAKERYSWAP_FACTORY) + mstore(0xB15, salt) + mstore(0xB35, BAKERYSWAP_PAIR_INIT_CODE_HASH) + } + default { + mstore(0xB00, FF_SUSHISWAP_FACTORY) + mstore(0xB15, salt) + mstore(0xB35, SUSHISWAP_PAIR_INIT_CODE_HASH) + } + pair := and(ADDRESS_MASK, keccak256(0xB00, 0x55)) + } + + // Revert with the return data from the most recent call. + function bubbleRevert() { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + + // Move `amount` tokens from the taker/caller to `to`. + function moveTakerTokensTo(token, to, amount) { + // Perform a `transferFrom()` + mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32) + mstore(0xB04, caller()) + mstore(0xB24, to) + mstore(0xB44, amount) + + let success := call( + gas(), + token, + 0, + 0xB00, + 0x64, + 0xC00, + // Copy only the first 32 bytes of return data. We + // only care about reading a boolean in the success + // case. We will use returndatacopy() in the failure case. + 0x20 + ) + + let rdsize := returndatasize() + + // Check for ERC20 success. ERC20 tokens should + // return a boolean, but some return nothing or + // extra data. We accept 0-length return data as + // success, or at least 32 bytes that starts with + // a 32-byte boolean true. + success := and( + success, // call itself succeeded + or( + iszero(rdsize), // no return data, or + and( + iszero(lt(rdsize, 32)), // at least 32 bytes + eq(mload(0xC00), 1) // starts with uint256(1) + ) + ) + ) + + if iszero(success) { + // Revert with the data returned from the transferFrom call. + returndatacopy(0, 0, rdsize) + revert(0, rdsize) + } + } + } + + // Revert if we bought too little. + require(buyAmount >= minBuyAmount, "PancakeSwapFeature/UnderBought"); + } +} diff --git a/contracts/zero-ex/contracts/src/features/TokenSpenderFeature.sol b/contracts/zero-ex/contracts/src/features/TokenSpenderFeature.sol deleted file mode 100644 index b2f58e70b4..0000000000 --- a/contracts/zero-ex/contracts/src/features/TokenSpenderFeature.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; -import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; -import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; -import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; -import "../errors/LibSpenderRichErrors.sol"; -import "../fixins/FixinCommon.sol"; -import "../migrations/LibMigrate.sol"; -import "../external/IAllowanceTarget.sol"; -import "../storage/LibTokenSpenderStorage.sol"; -import "./interfaces/IFeature.sol"; -import "./interfaces/ITokenSpenderFeature.sol"; - - -/// @dev Feature that allows spending token allowances. -contract TokenSpenderFeature is - IFeature, - ITokenSpenderFeature, - FixinCommon -{ - // solhint-disable - /// @dev Name of this feature. - string public constant override FEATURE_NAME = "TokenSpender"; - /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); - // solhint-enable - - using LibRichErrorsV06 for bytes; - - /// @dev Initialize and register this feature. Should be delegatecalled - /// into during a `Migrate.migrate()`. - /// @param allowanceTarget An `allowanceTarget` instance, configured to have - /// the ZeroeEx contract as an authority. - /// @return success `MIGRATE_SUCCESS` on success. - function migrate(IAllowanceTarget allowanceTarget) - external - returns (bytes4 success) - { - LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget; - _registerFeatureFunction(this.getAllowanceTarget.selector); - _registerFeatureFunction(this._spendERC20Tokens.selector); - _registerFeatureFunction(this.getSpendableERC20BalanceOf.selector); - return LibMigrate.MIGRATE_SUCCESS; - } - - /// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within. - /// @param token The token to spend. - /// @param owner The owner of the tokens. - /// @param to The recipient of the tokens. - /// @param amount The amount of `token` to transfer. - function _spendERC20Tokens( - IERC20TokenV06 token, - address owner, - address to, - uint256 amount - ) - external - override - onlySelf - { - IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget; - // Have the allowance target execute an ERC20 `transferFrom()`. - (bool didSucceed, bytes memory resultData) = address(spender).call( - abi.encodeWithSelector( - IAllowanceTarget.executeCall.selector, - address(token), - abi.encodeWithSelector( - IERC20TokenV06.transferFrom.selector, - owner, - to, - amount - ) - ) - ); - if (didSucceed) { - resultData = abi.decode(resultData, (bytes)); - } - if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) { - LibSpenderRichErrors.SpenderERC20TransferFromFailedError( - address(token), - owner, - to, - amount, - resultData - ).rrevert(); - } - } - - /// @dev Gets the maximum amount of an ERC20 token `token` that can be - /// pulled from `owner` by the token spender. - /// @param token The token to spend. - /// @param owner The owner of the tokens. - /// @return amount The amount of tokens that can be pulled. - function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner) - external - override - view - returns (uint256 amount) - { - return LibSafeMathV06.min256( - token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)), - token.balanceOf(owner) - ); - } - - /// @dev Get the address of the allowance target. - /// @return target The target of token allowances. - function getAllowanceTarget() - external - override - view - returns (address target) - { - return address(LibTokenSpenderStorage.getStorage().allowanceTarget); - } -} diff --git a/contracts/zero-ex/contracts/src/features/TransformERC20Feature.sol b/contracts/zero-ex/contracts/src/features/TransformERC20Feature.sol index 5ed06afbe5..da283aa037 100644 --- a/contracts/zero-ex/contracts/src/features/TransformERC20Feature.sol +++ b/contracts/zero-ex/contracts/src/features/TransformERC20Feature.sol @@ -60,10 +60,7 @@ contract TransformERC20Feature is /// @dev Version of this feature. uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1); - constructor(bytes32 greedyTokensBloomFilter) - public - FixinTokenSpender(greedyTokensBloomFilter) - {} + constructor() public {} /// @dev Initialize and register this feature. /// Should be delegatecalled by `Migrate.migrate()`. diff --git a/contracts/zero-ex/contracts/src/features/UniswapFeature.sol b/contracts/zero-ex/contracts/src/features/UniswapFeature.sol index b44ce54974..d4fe1ca410 100644 --- a/contracts/zero-ex/contracts/src/features/UniswapFeature.sol +++ b/contracts/zero-ex/contracts/src/features/UniswapFeature.sol @@ -23,7 +23,6 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; import "../migrations/LibMigrate.sol"; -import "../external/IAllowanceTarget.sol"; import "../fixins/FixinCommon.sol"; import "./interfaces/IFeature.sol"; import "./interfaces/IUniswapFeature.sol"; @@ -38,13 +37,9 @@ contract UniswapFeature is /// @dev Name of this feature. string public constant override FEATURE_NAME = "UniswapFeature"; /// @dev Version of this feature. - uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1); - /// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails. - bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER; + uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 2); /// @dev WETH contract. IEtherTokenV06 private immutable WETH; - /// @dev AllowanceTarget instance. - IAllowanceTarget private immutable ALLOWANCE_TARGET; // 0xFF + address of the UniswapV2Factory contract. uint256 constant private FF_UNISWAP_FACTORY = 0xFF5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f0000000000000000000000; @@ -80,16 +75,8 @@ contract UniswapFeature is /// @dev Construct this contract. /// @param weth The WETH contract. - /// @param allowanceTarget The AllowanceTarget contract. - /// @param greedyTokensBloomFilter The bloom filter for greedy tokens. - constructor( - IEtherTokenV06 weth, - IAllowanceTarget allowanceTarget, - bytes32 greedyTokensBloomFilter - ) public { + constructor(IEtherTokenV06 weth) public { WETH = weth; - ALLOWANCE_TARGET = allowanceTarget; - GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter; } /// @dev Initialize and register this feature. @@ -124,8 +111,6 @@ contract UniswapFeature is { // Load immutables onto the stack. IEtherTokenV06 weth = WETH; - IAllowanceTarget allowanceTarget = ALLOWANCE_TARGET; - bytes32 greedyTokensBloomFilter = GREEDY_TOKENS_BLOOM_FILTER; // Store some vars in memory to get around stack limits. assembly { @@ -135,10 +120,6 @@ contract UniswapFeature is mstore(0xA20, isSushi) // mload(0xA40) == WETH mstore(0xA40, weth) - // mload(0xA60) == ALLOWANCE_TARGET - mstore(0xA60, allowanceTarget) - // mload(0xA80) == GREEDY_TOKENS_BLOOM_FILTER - mstore(0xA80, greedyTokensBloomFilter) } } @@ -373,38 +354,7 @@ contract UniswapFeature is // Move `amount` tokens from the taker/caller to `to`. function moveTakerTokensTo(token, to, amount) { - - // If the token is possibly greedy, we check the allowance rather - // than relying on letting the transferFrom() call fail and - // falling through to legacy allowance target because the token - // will eat all our gas. - if isTokenPossiblyGreedy(token) { - // Check if we have enough direct allowance by calling - // `token.allowance()` - mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32) - mstore(0xB04, caller()) - mstore(0xB24, address()) - let success := staticcall(gas(), token, 0xB00, 0x44, 0xC00, 0x20) - if iszero(success) { - // Call to allowance() failed. - bubbleRevert() - } - // Make sure the allowance call returned at least a word. - if lt(returndatasize(), 0x20) { - revert(0, 0) - } - // Call succeeded. - // Result is stored in 0xC00-0xC20. - if lt(mload(0xC00), amount) { - // We don't have enough direct allowance, so try - // going through the legacy allowance taregt. - moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) - leave - } - } - - // Otherwise we will optimistically try to perform a `transferFrom()` - // directly then if it fails we will go through the legacy allowance target. + // Perform a `transferFrom()` mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32) mstore(0xB04, caller()) mstore(0xB24, to) @@ -419,8 +369,7 @@ contract UniswapFeature is 0xC00, // Copy only the first 32 bytes of return data. We // only care about reading a boolean in the success - // case, and we discard the return data in the - // failure case. + // case. We will use returndatacopy() in the failure case. 0x20 ) @@ -443,37 +392,11 @@ contract UniswapFeature is ) if iszero(success) { - // Try to fall back to the allowance target. - moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) + // Revert with the data returned from the transferFrom call. + returndatacopy(0, 0, rdsize) + revert(0, rdsize) } } - - // Move tokens by going through the legacy allowance target contract. - function moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) { - mstore(0xB00, ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32) - mstore(0xB04, token) - mstore(0xB24, 0x40) - mstore(0xB44, 0x64) - mstore(0xB64, TRANSFER_FROM_CALL_SELECTOR_32) - mstore(0xB68, caller()) - mstore(0xB88, to) - mstore(0xBA8, amount) - if iszero(call(gas(), mload(0xA60), 0, 0xB00, 0xC8, 0x00, 0x0)) { - bubbleRevert() - } - // If this fall back failed, the swap will most likely fail - // so there's no need to validate the result. - } - - // Checks if a token possibly belongs to the GREEDY_TOKENS_BLOOM_FILTER - // bloom filter. - function isTokenPossiblyGreedy(token) -> isPossiblyGreedy { - // The hash is given by: - // (1 << (keccak256(token) % 256)) | (1 << (token % 256)) - mstore(0, token) - let h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1)) - isPossiblyGreedy := eq(and(h, mload(0xA80)), h) - } } // Revert if we bought too little. diff --git a/contracts/zero-ex/contracts/src/features/interfaces/IPancakeSwapFeature.sol b/contracts/zero-ex/contracts/src/features/interfaces/IPancakeSwapFeature.sol new file mode 100644 index 0000000000..611cf0fb57 --- /dev/null +++ b/contracts/zero-ex/contracts/src/features/interfaces/IPancakeSwapFeature.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2021 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.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; + + +/// @dev VIP PancakeSwap/BakerySwap/SushiSwap fill functions. +interface IPancakeSwapFeature { + + enum ProtocolFork { + PancakeSwap, + BakerySwap, + SushiSwap + } + + /// @dev Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap. + /// @param tokens Sell path. + /// @param sellAmount of `tokens[0]` Amount to sell. + /// @param minBuyAmount Minimum amount of `tokens[-1]` to buy. + /// @param fork The protocol fork to use. + /// @return buyAmount Amount of `tokens[-1]` bought. + function sellToPancakeSwap( + IERC20TokenV06[] calldata tokens, + uint256 sellAmount, + uint256 minBuyAmount, + ProtocolFork fork + ) + external + payable + returns (uint256 buyAmount); +} diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol index 69b8f5f929..b90f0a9c91 100644 --- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol +++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol @@ -40,11 +40,10 @@ abstract contract NativeOrdersCancellation is uint256 private constant HIGH_BIT = 1 << 255; constructor( - address zeroExAddress, - bytes32 greedyTokensBloomFilter + address zeroExAddress ) internal - NativeOrdersInfo(zeroExAddress, greedyTokensBloomFilter) + NativeOrdersInfo(zeroExAddress) { // solhint-disable no-empty-blocks } diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol index 6f80cf6162..47d6fde3ba 100644 --- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol +++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol @@ -51,12 +51,10 @@ abstract contract NativeOrdersInfo is uint256 private constant HIGH_BIT = 1 << 255; constructor( - address zeroExAddress, - bytes32 greedyTokensBloomFilter + address zeroExAddress ) internal FixinEIP712(zeroExAddress) - FixinTokenSpender(greedyTokensBloomFilter) { // solhint-disable no-empty-blocks } diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol index 106f18956a..7c030e1840 100644 --- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol +++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol @@ -96,11 +96,10 @@ abstract contract NativeOrdersSettlement is IEtherTokenV06 weth, IStaking staking, FeeCollectorController feeCollectorController, - uint32 protocolFeeMultiplier, - bytes32 greedyTokensBloomFilter + uint32 protocolFeeMultiplier ) public - NativeOrdersCancellation(zeroExAddress, greedyTokensBloomFilter) + NativeOrdersCancellation(zeroExAddress) NativeOrdersProtocolFees(weth, staking, feeCollectorController, protocolFeeMultiplier) { // solhint-disable no-empty-blocks diff --git a/contracts/zero-ex/contracts/src/fixins/FixinTokenSpender.sol b/contracts/zero-ex/contracts/src/fixins/FixinTokenSpender.sol index 49f67f6760..e411f58eb8 100644 --- a/contracts/zero-ex/contracts/src/fixins/FixinTokenSpender.sol +++ b/contracts/zero-ex/contracts/src/fixins/FixinTokenSpender.sol @@ -22,28 +22,13 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; -import "../features/interfaces/ITokenSpenderFeature.sol"; -import "../errors/LibSpenderRichErrors.sol"; -import "../external/FeeCollector.sol"; -import "../vendor/v3/IStaking.sol"; -import "../vendor/v3/IStaking.sol"; /// @dev Helpers for moving tokens around. abstract contract FixinTokenSpender { - using LibRichErrorsV06 for bytes; // Mask of the lower 20 bytes of a bytes32. uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; - /// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails. - bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER; - - /// @param greedyTokensBloomFilter The bloom filter for all greedy tokens. - constructor(bytes32 greedyTokensBloomFilter) - internal - { - GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter; - } /// @dev Transfers ERC20 tokens from `owner` to `to`. /// @param token The token to spend. @@ -58,29 +43,8 @@ abstract contract FixinTokenSpender { ) internal { - bool success; - bytes memory revertData; - require(address(token) != address(this), "FixinTokenSpender/CANNOT_INVOKE_SELF"); - // If the token eats all gas when failing, we do not want to perform - // optimistic fall through to the old AllowanceTarget contract if the - // direct transferFrom() fails. - if (_isTokenPossiblyGreedy(token)) { - // If the token does not have a direct allowance on us then we use - // the allowance target. - if (token.allowance(owner, address(this)) < amount) { - _transferFromLegacyAllowanceTarget( - token, - owner, - to, - amount, - "" - ); - return; - } - } - assembly { let ptr := mload(0x40) // free memory pointer @@ -90,20 +54,18 @@ abstract contract FixinTokenSpender { mstore(add(ptr, 0x24), and(to, ADDRESS_MASK)) mstore(add(ptr, 0x44), amount) - success := call( + let success := call( gas(), and(token, ADDRESS_MASK), 0, ptr, 0x64, - 0, - 0 + ptr, + 32 ) let rdsize := returndatasize() - returndatacopy(add(ptr, 0x20), 0, rdsize) // reuse memory - // Check for ERC20 success. ERC20 tokens should return a boolean, // but some don't. We accept 0-length return data as success, or at // least 32 bytes that starts with a 32-byte boolean true. @@ -113,30 +75,16 @@ abstract contract FixinTokenSpender { iszero(rdsize), // no return data, or and( iszero(lt(rdsize, 32)), // at least 32 bytes - eq(mload(add(ptr, 0x20)), 1) // starts with uint256(1) + eq(mload(ptr), 1) // starts with uint256(1) ) ) ) if iszero(success) { - // revertData is a bytes, so length-prefixed data - mstore(ptr, rdsize) - revertData := ptr - - // update free memory pointer (ptr + 32-byte length + return data) - mstore(0x40, add(add(ptr, 0x20), rdsize)) + returndatacopy(ptr, 0, rdsize) + revert(ptr, rdsize) } } - - if (!success) { - _transferFromLegacyAllowanceTarget( - token, - owner, - to, - amount, - revertData - ); - } } /// @dev Gets the maximum amount of an ERC20 token `token` that can be @@ -157,53 +105,4 @@ abstract contract FixinTokenSpender { token.balanceOf(owner) ); } - - /// @dev Check if a token possibly belongs to the `GREEDY_TOKENS_BLOOM_FILTER` - /// bloom filter. - function _isTokenPossiblyGreedy(IERC20TokenV06 token) - internal - view - returns (bool isPossiblyGreedy) - { - // The hash is given by: - // (1 << (keccak256(token) % 256)) | (1 << (token % 256)) - bytes32 h; - assembly { - mstore(0, token) - h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1)) - } - return (h & GREEDY_TOKENS_BLOOM_FILTER) == h; - } - - /// @dev Transfer tokens using the legacy allowance target instead of - /// allowances directly set on the exchange proxy. - function _transferFromLegacyAllowanceTarget( - IERC20TokenV06 token, - address owner, - address to, - uint256 amount, - bytes memory initialRevertData - ) - private - { - // Try the old AllowanceTarget. - try ITokenSpenderFeature(address(this))._spendERC20Tokens( - token, - owner, - to, - amount - ) { - } catch (bytes memory revertData) { - // Bubble up the first error message. (In general, the fallback to the - // allowance target is opportunistic. We ignore the specific error - // message if it fails.) - LibSpenderRichErrors.SpenderERC20TransferFromFailedError( - address(token), - owner, - to, - amount, - initialRevertData.length != 0 ? initialRevertData : revertData - ).rrevert(); - } - } } diff --git a/contracts/zero-ex/contracts/src/migrations/FullMigration.sol b/contracts/zero-ex/contracts/src/migrations/FullMigration.sol index 39ed12c9d2..45fc93721f 100644 --- a/contracts/zero-ex/contracts/src/migrations/FullMigration.sol +++ b/contracts/zero-ex/contracts/src/migrations/FullMigration.sol @@ -22,11 +22,9 @@ pragma experimental ABIEncoderV2; import "../ZeroEx.sol"; import "../features/interfaces/IOwnableFeature.sol"; -import "../features/TokenSpenderFeature.sol"; import "../features/TransformERC20Feature.sol"; import "../features/MetaTransactionsFeature.sol"; import "../features/NativeOrdersFeature.sol"; -import "../external/AllowanceTarget.sol"; import "./InitialMigration.sol"; @@ -39,7 +37,6 @@ contract FullMigration { struct Features { SimpleFunctionRegistryFeature registry; OwnableFeature ownable; - TokenSpenderFeature tokenSpender; TransformERC20Feature transformERC20; MetaTransactionsFeature metaTransactions; NativeOrdersFeature nativeOrders; @@ -107,7 +104,7 @@ contract FullMigration { ); // Add features. - _addFeatures(zeroEx, owner, features, migrateOpts); + _addFeatures(zeroEx, features, migrateOpts); // Transfer ownership to the real owner. IOwnableFeature(address(zeroEx)).transferOwnership(owner); @@ -132,36 +129,16 @@ contract FullMigration { /// @dev Deploy and register features to the ZeroEx contract. /// @param zeroEx The bootstrapped ZeroEx contract. - /// @param owner The ultimate owner of the ZeroEx contract. /// @param features Features to add to the proxy. /// @param migrateOpts Parameters needed to initialize features. function _addFeatures( ZeroEx zeroEx, - address owner, Features memory features, MigrateOpts memory migrateOpts ) private { IOwnableFeature ownable = IOwnableFeature(address(zeroEx)); - // TokenSpenderFeature - { - // Create the allowance target. - AllowanceTarget allowanceTarget = new AllowanceTarget(); - // Let the ZeroEx contract use the allowance target. - allowanceTarget.addAuthorizedAddress(address(zeroEx)); - // Transfer ownership of the allowance target to the (real) owner. - allowanceTarget.transferOwnership(owner); - // Register the feature. - ownable.migrate( - address(features.tokenSpender), - abi.encodeWithSelector( - TokenSpenderFeature.migrate.selector, - allowanceTarget - ), - address(this) - ); - } // TransformERC20Feature { // Register the feature. diff --git a/contracts/zero-ex/contracts/src/storage/LibTokenSpenderStorage.sol b/contracts/zero-ex/contracts/src/storage/LibTokenSpenderStorage.sol deleted file mode 100644 index f8a3bc1c82..0000000000 --- a/contracts/zero-ex/contracts/src/storage/LibTokenSpenderStorage.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "./LibStorage.sol"; -import "../external/IAllowanceTarget.sol"; - - -/// @dev Storage helpers for the `TokenSpender` feature. -library LibTokenSpenderStorage { - - /// @dev Storage bucket for this feature. - struct Storage { - // Allowance target contract. - IAllowanceTarget allowanceTarget; - } - - /// @dev Get the storage bucket for this contract. - function getStorage() internal pure returns (Storage storage stor) { - uint256 storageSlot = LibStorage.getStorageSlot( - LibStorage.StorageId.TokenSpender - ); - // Dip into assembly to change the slot pointed to by the local - // variable `stor`. - // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries - assembly { stor_slot := storageSlot } - } -} diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index 1db297a0dd..8db42204d8 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 /* - Copyright 2020 ZeroEx Intl. + Copyright 2021 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ pragma solidity ^0.6.5; pragma experimental ABIEncoderV2; import "./IBridgeAdapter.sol"; -import "./BridgeSource.sol"; +import "./BridgeProtocols.sol"; import "./mixins/MixinBalancer.sol"; import "./mixins/MixinBancor.sol"; import "./mixins/MixinCoFiX.sol"; @@ -32,9 +32,9 @@ import "./mixins/MixinDodoV2.sol"; import "./mixins/MixinKyber.sol"; import "./mixins/MixinMooniswap.sol"; import "./mixins/MixinMStable.sol"; +import "./mixins/MixinNerve.sol"; import "./mixins/MixinOasis.sol"; import "./mixins/MixinShell.sol"; -import "./mixins/MixinSushiswap.sol"; import "./mixins/MixinUniswap.sol"; import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinZeroExBridge.sol"; @@ -51,9 +51,9 @@ contract BridgeAdapter is MixinKyber, MixinMooniswap, MixinMStable, + MixinNerve, MixinOasis, MixinShell, - MixinSushiswap, MixinUniswap, MixinUniswapV2, MixinZeroExBridge @@ -70,9 +70,9 @@ contract BridgeAdapter is MixinKyber(weth) MixinMooniswap(weth) MixinMStable() + MixinNerve() MixinOasis() MixinShell() - MixinSushiswap() MixinUniswap(weth) MixinUniswapV2() MixinZeroExBridge() @@ -88,109 +88,106 @@ contract BridgeAdapter is override returns (uint256 boughtAmount) { - if (order.source == BridgeSource.CURVE || - order.source == BridgeSource.SWERVE || - order.source == BridgeSource.SNOWSWAP) { + uint128 protocolId = uint128(uint256(order.source) >> 128); + if (protocolId == BridgeProtocols.CURVE) { boughtAmount = _tradeCurve( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.SUSHISWAP) { - boughtAmount = _tradeSushiswap( - buyToken, - sellAmount, - order.bridgeData - ); - } else if (order.source == BridgeSource.UNISWAPV2 || - order.source == BridgeSource.LINKSWAP) { + } else if (protocolId == BridgeProtocols.UNISWAPV2) { boughtAmount = _tradeUniswapV2( buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.UNISWAP) { + } else if (protocolId == BridgeProtocols.UNISWAP) { boughtAmount = _tradeUniswap( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.BALANCER || - order.source == BridgeSource.CREAM) { + } else if (protocolId == BridgeProtocols.BALANCER) { boughtAmount = _tradeBalancer( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.KYBER) { + } else if (protocolId == BridgeProtocols.KYBER) { boughtAmount = _tradeKyber( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.MOONISWAP) { + } else if (protocolId == BridgeProtocols.MOONISWAP) { boughtAmount = _tradeMooniswap( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.MSTABLE) { + } else if (protocolId == BridgeProtocols.MSTABLE) { boughtAmount = _tradeMStable( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.OASIS) { + } else if (protocolId == BridgeProtocols.OASIS) { boughtAmount = _tradeOasis( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.SHELL) { + } else if (protocolId == BridgeProtocols.SHELL) { boughtAmount = _tradeShell( sellToken, buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.DODO) { + } else if (protocolId == BridgeProtocols.DODO) { boughtAmount = _tradeDodo( sellToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.DODOV2) { + } else if (protocolId == BridgeProtocols.DODOV2) { boughtAmount = _tradeDodoV2( sellToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.CRYPTOCOM) { + } else if (protocolId == BridgeProtocols.CRYPTOCOM) { boughtAmount = _tradeCryptoCom( buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.BANCOR) { + } else if (protocolId == BridgeProtocols.BANCOR) { boughtAmount = _tradeBancor( buyToken, sellAmount, order.bridgeData ); - } else if (order.source == BridgeSource.COFIX) { + } else if (protocolId == BridgeProtocols.COFIX) { boughtAmount = _tradeCoFiX( sellToken, buyToken, sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.NERVE) { + boughtAmount = _tradeNerve( + sellToken, + sellAmount, + order.bridgeData + ); } else { boughtAmount = _tradeZeroExBridge( sellToken, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol new file mode 100644 index 0000000000..baebf8ac05 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2021 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.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; + + +library BridgeProtocols { + // A incrementally increasing, append-only list of protocol IDs. + // We don't use an enum so solidity doesn't throw when we pass in a + // new protocol ID that hasn't been rolled up yet. + uint128 internal constant UNKNOWN = 0; + uint128 internal constant CURVE = 1; + uint128 internal constant UNISWAPV2 = 2; + uint128 internal constant UNISWAP = 3; + uint128 internal constant BALANCER = 4; + uint128 internal constant KYBER = 5; + uint128 internal constant MOONISWAP = 6; + uint128 internal constant MSTABLE = 7; + uint128 internal constant OASIS = 8; + uint128 internal constant SHELL = 9; + uint128 internal constant DODO = 10; + uint128 internal constant DODOV2 = 11; + uint128 internal constant CRYPTOCOM = 12; + uint128 internal constant BANCOR = 13; + uint128 internal constant COFIX = 14; + uint128 internal constant NERVE = 15; +} diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeSource.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeSource.sol deleted file mode 100644 index 3835ea6ca8..0000000000 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeSource.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - - -library BridgeSource { - uint256 constant internal BALANCER = 0; - uint256 constant internal BANCOR = 1; - uint256 constant internal COFIX = 2; - uint256 constant internal CURVE = 3; - uint256 constant internal CREAM = 4; - uint256 constant internal CRYPTOCOM = 5; - uint256 constant internal DODO = 6; - uint256 constant internal KYBER = 7; - uint256 constant internal LIQUIDITYPROVIDER = 8; - uint256 constant internal MOONISWAP = 9; - uint256 constant internal MSTABLE = 10; - uint256 constant internal OASIS = 11; - uint256 constant internal SHELL = 12; - uint256 constant internal SNOWSWAP = 13; - uint256 constant internal SUSHISWAP = 14; - uint256 constant internal SWERVE = 15; - uint256 constant internal UNISWAP = 16; - uint256 constant internal UNISWAPV2 = 17; - uint256 constant internal DODOV2 = 18; - uint256 constant internal LINKSWAP = 19; - // New sources should be APPENDED to this list, taking the next highest - // integer value. -} diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/IBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/IBridgeAdapter.sol index d83d11ada8..a9f4dadd83 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/IBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/IBridgeAdapter.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 /* - Copyright 2020 ZeroEx Intl. + Copyright 2021 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,20 +26,24 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; interface IBridgeAdapter { struct BridgeOrder { - uint256 source; + // Upper 16 bytes: uint128 protocol ID (right-aligned) + // Lower 16 bytes: ASCII source name (left-aligned) + bytes32 source; uint256 takerTokenAmount; uint256 makerTokenAmount; bytes bridgeData; } /// @dev Emitted when tokens are swapped with an external source. - /// @param source The unique ID for the source. See `BridgeSource.sol` + /// @param source A unique ID for the source, where the upper 16 bytes + /// encodes the (right-aligned) uint128 protocol ID and the + /// lower 16 bytes encodes an ASCII source name. /// @param inputToken The token the bridge is converting from. /// @param outputToken The token the bridge is converting to. /// @param inputTokenAmount Amount of input token sold. /// @param outputTokenAmount Amount of output token bought. event BridgeFill( - uint256 source, + bytes32 source, IERC20TokenV06 inputToken, IERC20TokenV06 outputToken, uint256 inputTokenAmount, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinNerve.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinNerve.sol new file mode 100644 index 0000000000..6c42ba5ce9 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinNerve.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + 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.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; + +contract MixinNerve { + + using LibERC20TokenV06 for IERC20TokenV06; + using LibSafeMathV06 for uint256; + using LibRichErrorsV06 for bytes; + + + struct NerveBridgeData { + address pool; + bytes4 exchangeFunctionSelector; + int128 fromCoinIdx; + int128 toCoinIdx; + } + + function _tradeNerve( + IERC20TokenV06 sellToken, + uint256 sellAmount, + bytes memory bridgeData + ) + internal + returns (uint256 boughtAmount) + { + // Basically a Curve fork but the swap option has a deadline + + // Decode the bridge data to get the Curve metadata. + NerveBridgeData memory data = abi.decode(bridgeData, (NerveBridgeData)); + sellToken.approveIfBelow(data.pool, sellAmount); + (bool success, bytes memory resultData) = + data.pool.call(abi.encodeWithSelector( + data.exchangeFunctionSelector, + data.fromCoinIdx, + data.toCoinIdx, + // dx + sellAmount, + // min dy + 1, + // deadline + block.timestamp + )); + if (!success) { + resultData.rrevert(); + } + return abi.decode(resultData, (uint256)); + } +} diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinSushiswap.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinSushiswap.sol deleted file mode 100644 index 1fc8f6a682..0000000000 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinSushiswap.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; -import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; -import "./MixinUniswapV2.sol"; - -contract MixinSushiswap { - - using LibERC20TokenV06 for IERC20TokenV06; - - function _tradeSushiswap( - IERC20TokenV06 buyToken, - uint256 sellAmount, - bytes memory bridgeData - ) - internal - returns (uint256 boughtAmount) - { - IERC20TokenV06[] memory path; - IUniswapV2Router02 router; - { - address[] memory _path; - (router, _path) = - abi.decode(bridgeData, (IUniswapV2Router02, address[])); - // To get around `abi.decode()` not supporting interface array types. - assembly { path := _path } - } - - require(path.length >= 2, "MixinSushiswap/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); - require( - path[path.length - 1] == buyToken, - "MixinSushiswap/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" - ); - // Grant the Uniswap router an allowance to sell the first token. - path[0].approveIfBelow( - address(router), - sellAmount - ); - - uint[] memory amounts = router.swapExactTokensForTokens( - // Sell all tokens we hold. - sellAmount, - // Minimum buy amount. - 1, - // Convert to `buyToken` along this path. - path, - // Recipient is `this`. - address(this), - // Expires after this block. - block.timestamp - ); - return amounts[amounts.length-1]; - } -} diff --git a/contracts/zero-ex/contracts/test/TestFixinTokenSpender.sol b/contracts/zero-ex/contracts/test/TestFixinTokenSpender.sol index a2f357c29e..30245f5682 100644 --- a/contracts/zero-ex/contracts/test/TestFixinTokenSpender.sol +++ b/contracts/zero-ex/contracts/test/TestFixinTokenSpender.sol @@ -26,12 +26,7 @@ import "../src/fixins/FixinTokenSpender.sol"; contract TestFixinTokenSpender is FixinTokenSpender { - uint256 constant private TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340; - - constructor(bytes32 greedyTokensBloomFilter) - public - FixinTokenSpender(greedyTokensBloomFilter) - {} + constructor() public {} function transferERC20Tokens( IERC20TokenV06 token, @@ -56,21 +51,6 @@ contract TestFixinTokenSpender is uint256 amount ); - // This is called as a fallback when the original transferFrom() fails. - function _spendERC20Tokens( - IERC20TokenV06 token, - address owner, - address to, - uint256 amount - ) - external - { - require(amount == TRIGGER_FALLBACK_SUCCESS_AMOUNT, - "TokenSpenderFallback/FAILURE_AMOUNT"); - - emit FallbackCalled(address(token), owner, to, amount); - } - function getSpendableERC20BalanceOf( IERC20TokenV06 token, address owner @@ -81,12 +61,4 @@ contract TestFixinTokenSpender is { return _getSpendableERC20BalanceOf(token, owner); } - - function isTokenPossiblyGreedy(IERC20TokenV06 token) - external - view - returns (bool) - { - return _isTokenPossiblyGreedy(token); - } } diff --git a/contracts/zero-ex/contracts/test/TestMetaTransactionsNativeOrdersFeature.sol b/contracts/zero-ex/contracts/test/TestMetaTransactionsNativeOrdersFeature.sol index 200b1352a0..eb1d3ee3ac 100644 --- a/contracts/zero-ex/contracts/test/TestMetaTransactionsNativeOrdersFeature.sol +++ b/contracts/zero-ex/contracts/test/TestMetaTransactionsNativeOrdersFeature.sol @@ -35,8 +35,7 @@ contract TestMetaTransactionsNativeOrdersFeature is IEtherTokenV06(0), IStaking(0), FeeCollectorController(address(new TestFeeCollectorController())), - 0, - bytes32(0) + 0 ) {} diff --git a/contracts/zero-ex/contracts/test/TestMetaTransactionsTransformERC20Feature.sol b/contracts/zero-ex/contracts/test/TestMetaTransactionsTransformERC20Feature.sol index 6d23c160a1..49437cf833 100644 --- a/contracts/zero-ex/contracts/test/TestMetaTransactionsTransformERC20Feature.sol +++ b/contracts/zero-ex/contracts/test/TestMetaTransactionsTransformERC20Feature.sol @@ -38,7 +38,7 @@ contract TestMetaTransactionsTransformERC20Feature is Transformation[] transformations ); - constructor() public TransformERC20Feature(0) {} + constructor() public TransformERC20Feature() {} function _transformERC20(TransformERC20Args memory args) public diff --git a/contracts/zero-ex/contracts/test/TestNativeOrdersFeature.sol b/contracts/zero-ex/contracts/test/TestNativeOrdersFeature.sol index 6b0fe6f958..b2db91d17b 100644 --- a/contracts/zero-ex/contracts/test/TestNativeOrdersFeature.sol +++ b/contracts/zero-ex/contracts/test/TestNativeOrdersFeature.sol @@ -13,8 +13,7 @@ contract TestNativeOrdersFeature is IEtherTokenV06 weth, IStaking staking, FeeCollectorController _feeCollectorController, // Unused but necessary for artifact compatibility. - uint32 protocolFeeMultiplier, - bytes32 greedyTokensBloomFilter + uint32 protocolFeeMultiplier ) public NativeOrdersFeature( @@ -22,8 +21,7 @@ contract TestNativeOrdersFeature is weth, staking, FeeCollectorController(address(new TestFeeCollectorController())), - protocolFeeMultiplier, - greedyTokensBloomFilter + protocolFeeMultiplier ) { // solhint-disable no-empty-blocks diff --git a/contracts/zero-ex/contracts/test/TestTokenSpender.sol b/contracts/zero-ex/contracts/test/TestTokenSpender.sol deleted file mode 100644 index d9c4974553..0000000000 --- a/contracts/zero-ex/contracts/test/TestTokenSpender.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6.5; -pragma experimental ABIEncoderV2; - -import "../src/features/TokenSpenderFeature.sol"; - -contract TestTokenSpender is - TokenSpenderFeature -{ - modifier onlySelf() override { - _; - } -} diff --git a/contracts/zero-ex/contracts/test/TestTransformERC20.sol b/contracts/zero-ex/contracts/test/TestTransformERC20.sol index e726796a4e..54a5c93217 100644 --- a/contracts/zero-ex/contracts/test/TestTransformERC20.sol +++ b/contracts/zero-ex/contracts/test/TestTransformERC20.sol @@ -30,5 +30,5 @@ contract TestTransformERC20 is _; } - constructor() public TransformERC20Feature(0) {} + constructor() public TransformERC20Feature() {} } diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index 176dcf0d1b..55b7649faa 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -41,9 +41,9 @@ "rollback": "node ./lib/scripts/rollback.js" }, "config": { - "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature", + "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeSource|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" + "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json" }, "repository": { "type": "git", diff --git a/contracts/zero-ex/src/artifacts.ts b/contracts/zero-ex/src/artifacts.ts index 2fbc57173d..ff378adcdc 100644 --- a/contracts/zero-ex/src/artifacts.ts +++ b/contracts/zero-ex/src/artifacts.ts @@ -23,7 +23,6 @@ import * as INativeOrdersFeature from '../generated-artifacts/INativeOrdersFeatu import * as InitialMigration from '../generated-artifacts/InitialMigration.json'; import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json'; import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json'; -import * as ITokenSpenderFeature from '../generated-artifacts/ITokenSpenderFeature.json'; import * as ITransformERC20Feature from '../generated-artifacts/ITransformERC20Feature.json'; import * as IZeroEx from '../generated-artifacts/IZeroEx.json'; import * as LiquidityProviderFeature from '../generated-artifacts/LiquidityProviderFeature.json'; @@ -35,7 +34,6 @@ import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json'; import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json'; import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json'; import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json'; -import * as TokenSpenderFeature from '../generated-artifacts/TokenSpenderFeature.json'; import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json'; import * as WethTransformer from '../generated-artifacts/WethTransformer.json'; import * as ZeroEx from '../generated-artifacts/ZeroEx.json'; @@ -49,7 +47,6 @@ export const artifacts = { IERC20Transformer: IERC20Transformer as ContractArtifact, IOwnableFeature: IOwnableFeature as ContractArtifact, ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact, - ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact, ITransformERC20Feature: ITransformERC20Feature as ContractArtifact, FillQuoteTransformer: FillQuoteTransformer as ContractArtifact, PayTakerTransformer: PayTakerTransformer as ContractArtifact, @@ -58,7 +55,6 @@ export const artifacts = { OwnableFeature: OwnableFeature as ContractArtifact, SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact, TransformERC20Feature: TransformERC20Feature as ContractArtifact, - TokenSpenderFeature: TokenSpenderFeature as ContractArtifact, AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact, MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact, LogMetadataTransformer: LogMetadataTransformer as ContractArtifact, diff --git a/contracts/zero-ex/src/index.ts b/contracts/zero-ex/src/index.ts index 4a91cb841d..f6d5778cde 100644 --- a/contracts/zero-ex/src/index.ts +++ b/contracts/zero-ex/src/index.ts @@ -41,7 +41,6 @@ export { IOwnableFeatureEvents, ISimpleFunctionRegistryFeatureContract, ISimpleFunctionRegistryFeatureEvents, - ITokenSpenderFeatureContract, ITransformERC20FeatureContract, IZeroExContract, LogMetadataTransformerContract, diff --git a/contracts/zero-ex/src/migration.ts b/contracts/zero-ex/src/migration.ts index 30e73bc19d..b6a560348e 100644 --- a/contracts/zero-ex/src/migration.ts +++ b/contracts/zero-ex/src/migration.ts @@ -5,7 +5,6 @@ import { TxData } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from './artifacts'; -import { ZERO_BYTES32 } from './constants'; import { FeeCollectorControllerContract, FullMigrationContract, @@ -15,7 +14,6 @@ import { NativeOrdersFeatureContract, OwnableFeatureContract, SimpleFunctionRegistryFeatureContract, - TokenSpenderFeatureContract, TransformERC20FeatureContract, ZeroExContract, } from './wrappers'; @@ -108,7 +106,6 @@ export async function initialMigrateAsync( * Addresses of features for a full deployment of the Exchange Proxy. */ export interface FullFeatures extends BootstrapFeatures { - tokenSpender: string; transformERC20: string; metaTransactions: string; nativeOrders: string; @@ -118,7 +115,6 @@ export interface FullFeatures extends BootstrapFeatures { * Artifacts to use when deploying full features. */ export interface FullFeatureArtifacts extends BootstrapFeatureArtifacts { - tokenSpender: SimpleContractArtifact; transformERC20: SimpleContractArtifact; metaTransactions: SimpleContractArtifact; nativeOrders: SimpleContractArtifact; @@ -133,7 +129,6 @@ export interface FullFeaturesDeployConfig { wethAddress: string; stakingAddress: string; protocolFeeMultiplier: number; - greedyTokensBloomFilter: string; } /** @@ -149,11 +144,9 @@ const DEFAULT_FULL_FEATURES_DEPLOY_CONFIG = { stakingAddress: NULL_ADDRESS, feeCollectorController: NULL_ADDRESS, protocolFeeMultiplier: 70e3, - greedyTokensBloomFilter: ZERO_BYTES32, }; const DEFAULT_FULL_FEATURES_ARTIFACTS = { - tokenSpender: artifacts.TokenSpenderFeature, transformERC20: artifacts.TransformERC20Feature, metaTransactions: artifacts.MetaTransactionsFeature, nativeOrders: artifacts.NativeOrdersFeature, @@ -187,14 +180,6 @@ export async function deployFullFeaturesAsync( } return { ...(await deployBootstrapFeaturesAsync(provider, txDefaults)), - tokenSpender: - features.tokenSpender || - (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync( - _featureArtifacts.tokenSpender, - provider, - txDefaults, - artifacts, - )).address, transformERC20: features.transformERC20 || (await TransformERC20FeatureContract.deployFrom0xArtifactAsync( @@ -202,7 +187,6 @@ export async function deployFullFeaturesAsync( provider, txDefaults, artifacts, - _config.greedyTokensBloomFilter, )).address, metaTransactions: features.metaTransactions || @@ -212,7 +196,6 @@ export async function deployFullFeaturesAsync( txDefaults, artifacts, _config.zeroExAddress, - _config.greedyTokensBloomFilter, )).address, nativeOrders: features.nativeOrders || @@ -226,7 +209,6 @@ export async function deployFullFeaturesAsync( _config.stakingAddress, _config.feeCollectorController, _config.protocolFeeMultiplier, - _config.greedyTokensBloomFilter, )).address, }; } diff --git a/contracts/zero-ex/src/wrappers.ts b/contracts/zero-ex/src/wrappers.ts index 5a2265a37e..e853680874 100644 --- a/contracts/zero-ex/src/wrappers.ts +++ b/contracts/zero-ex/src/wrappers.ts @@ -20,7 +20,6 @@ export * from '../generated-wrappers/i_multiplex_feature'; export * from '../generated-wrappers/i_native_orders_feature'; export * from '../generated-wrappers/i_ownable_feature'; export * from '../generated-wrappers/i_simple_function_registry_feature'; -export * from '../generated-wrappers/i_token_spender_feature'; export * from '../generated-wrappers/i_transform_erc20_feature'; export * from '../generated-wrappers/i_zero_ex'; export * from '../generated-wrappers/initial_migration'; @@ -33,7 +32,6 @@ export * from '../generated-wrappers/ownable_feature'; export * from '../generated-wrappers/pay_taker_transformer'; export * from '../generated-wrappers/positive_slippage_fee_transformer'; export * from '../generated-wrappers/simple_function_registry_feature'; -export * from '../generated-wrappers/token_spender_feature'; export * from '../generated-wrappers/transform_erc20_feature'; export * from '../generated-wrappers/weth_transformer'; export * from '../generated-wrappers/zero_ex'; diff --git a/contracts/zero-ex/test/allowance_target_test.ts b/contracts/zero-ex/test/allowance_target_test.ts deleted file mode 100644 index 270a1bfb63..0000000000 --- a/contracts/zero-ex/test/allowance_target_test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils'; -import { AuthorizableRevertErrors, hexUtils, StringRevertError } from '@0x/utils'; - -import { artifacts } from './artifacts'; -import { AllowanceTargetContract, TestCallTargetContract, TestCallTargetEvents } from './wrappers'; - -blockchainTests.resets('AllowanceTarget', env => { - let owner: string; - let authority: string; - let allowanceTarget: AllowanceTargetContract; - let callTarget: TestCallTargetContract; - - before(async () => { - [owner, authority] = await env.getAccountAddressesAsync(); - allowanceTarget = await AllowanceTargetContract.deployFrom0xArtifactAsync( - artifacts.AllowanceTarget, - env.provider, - env.txDefaults, - artifacts, - ); - await allowanceTarget.addAuthorizedAddress(authority).awaitTransactionSuccessAsync(); - callTarget = await TestCallTargetContract.deployFrom0xArtifactAsync( - artifacts.TestCallTarget, - env.provider, - env.txDefaults, - artifacts, - ); - }); - - const TARGET_RETURN_VALUE = hexUtils.rightPad('0x12345678'); - const REVERTING_DATA = '0x1337'; - - describe('executeCall()', () => { - it('non-authority cannot call executeCall()', async () => { - const notAuthority = randomAddress(); - const tx = allowanceTarget - .executeCall(randomAddress(), hexUtils.random()) - .callAsync({ from: notAuthority }); - return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthority)); - }); - - it('authority can call executeCall()', async () => { - const targetData = hexUtils.random(128); - const receipt = await allowanceTarget - .executeCall(callTarget.address, targetData) - .awaitTransactionSuccessAsync({ from: authority }); - verifyEventsFromLogs( - receipt.logs, - [ - { - context: callTarget.address, - sender: allowanceTarget.address, - data: targetData, - value: constants.ZERO_AMOUNT, - }, - ], - TestCallTargetEvents.CallTargetCalled, - ); - }); - - it('AllowanceTarget returns call result', async () => { - const result = await allowanceTarget - .executeCall(callTarget.address, hexUtils.random(128)) - .callAsync({ from: authority }); - expect(result).to.eq(TARGET_RETURN_VALUE); - }); - - it('AllowanceTarget returns raw call revert', async () => { - const tx = allowanceTarget.executeCall(callTarget.address, REVERTING_DATA).callAsync({ from: authority }); - return expect(tx).to.revertWith(new StringRevertError('TestCallTarget/REVERT')); - }); - - it('AllowanceTarget cannot receive ETH', async () => { - const tx = env.web3Wrapper.sendTransactionAsync({ - to: allowanceTarget.address, - from: owner, - value: 0, - }); - return expect(tx).to.eventually.be.rejected(); - }); - }); -}); diff --git a/contracts/zero-ex/test/artifacts.ts b/contracts/zero-ex/test/artifacts.ts index c0b54e2e7b..d190a8c4e0 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -6,11 +6,10 @@ import { ContractArtifact } from 'ethereum-types'; import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json'; -import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json'; import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json'; import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json'; import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json'; -import * as BridgeSource from '../test/generated-artifacts/BridgeSource.json'; +import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json'; import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json'; import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json'; import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json'; @@ -22,7 +21,6 @@ import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentran import * as FixinTokenSpender from '../test/generated-artifacts/FixinTokenSpender.json'; import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json'; import * as FullMigration from '../test/generated-artifacts/FullMigration.json'; -import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json'; import * as IBatchFillNativeOrdersFeature from '../test/generated-artifacts/IBatchFillNativeOrdersFeature.json'; import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeature.json'; import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json'; @@ -40,6 +38,7 @@ import * as INativeOrdersEvents from '../test/generated-artifacts/INativeOrdersE import * as INativeOrdersFeature from '../test/generated-artifacts/INativeOrdersFeature.json'; import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json'; import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json'; +import * as IPancakeSwapFeature from '../test/generated-artifacts/IPancakeSwapFeature.json'; import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISimpleFunctionRegistryFeature.json'; import * as IStaking from '../test/generated-artifacts/IStaking.json'; import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json'; @@ -68,9 +67,7 @@ import * as LibSignature from '../test/generated-artifacts/LibSignature.json'; import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json'; import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json'; import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json'; -import * as LibSpenderRichErrors from '../test/generated-artifacts/LibSpenderRichErrors.json'; import * as LibStorage from '../test/generated-artifacts/LibStorage.json'; -import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpenderStorage.json'; import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json'; import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json'; import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json'; @@ -88,9 +85,9 @@ import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json'; import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json'; import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json'; import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json'; +import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json'; import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json'; import * as MixinShell from '../test/generated-artifacts/MixinShell.json'; -import * as MixinSushiswap from '../test/generated-artifacts/MixinSushiswap.json'; import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json'; import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json'; import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json'; @@ -102,6 +99,7 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo. import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json'; import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json'; import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json'; +import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json'; import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json'; import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json'; import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json'; @@ -134,7 +132,6 @@ import * as TestRfqOriginRegistration from '../test/generated-artifacts/TestRfqO import * as TestSimpleFunctionRegistryFeatureImpl1 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json'; import * as TestSimpleFunctionRegistryFeatureImpl2 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json'; import * as TestStaking from '../test/generated-artifacts/TestStaking.json'; -import * as TestTokenSpender from '../test/generated-artifacts/TestTokenSpender.json'; import * as TestTokenSpenderERC20Token from '../test/generated-artifacts/TestTokenSpenderERC20Token.json'; import * as TestTransformerBase from '../test/generated-artifacts/TestTransformerBase.json'; import * as TestTransformERC20 from '../test/generated-artifacts/TestTransformERC20.json'; @@ -143,7 +140,6 @@ import * as TestTransformerHost from '../test/generated-artifacts/TestTransforme import * as TestWeth from '../test/generated-artifacts/TestWeth.json'; import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json'; import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json'; -import * as TokenSpenderFeature from '../test/generated-artifacts/TokenSpenderFeature.json'; import * as Transformer from '../test/generated-artifacts/Transformer.json'; import * as TransformERC20Feature from '../test/generated-artifacts/TransformERC20Feature.json'; import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json'; @@ -163,14 +159,11 @@ export const artifacts = { LibProxyRichErrors: LibProxyRichErrors as ContractArtifact, LibSignatureRichErrors: LibSignatureRichErrors as ContractArtifact, LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact, - LibSpenderRichErrors: LibSpenderRichErrors as ContractArtifact, LibTransformERC20RichErrors: LibTransformERC20RichErrors as ContractArtifact, LibWalletRichErrors: LibWalletRichErrors as ContractArtifact, - AllowanceTarget: AllowanceTarget as ContractArtifact, FeeCollector: FeeCollector as ContractArtifact, FeeCollectorController: FeeCollectorController as ContractArtifact, FlashWallet: FlashWallet as ContractArtifact, - IAllowanceTarget: IAllowanceTarget as ContractArtifact, IFlashWallet: IFlashWallet as ContractArtifact, ILiquidityProviderSandbox: ILiquidityProviderSandbox as ContractArtifact, LibFeeCollector: LibFeeCollector as ContractArtifact, @@ -184,8 +177,8 @@ export const artifacts = { MultiplexFeature: MultiplexFeature as ContractArtifact, NativeOrdersFeature: NativeOrdersFeature as ContractArtifact, OwnableFeature: OwnableFeature as ContractArtifact, + PancakeSwapFeature: PancakeSwapFeature as ContractArtifact, SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact, - TokenSpenderFeature: TokenSpenderFeature as ContractArtifact, TransformERC20Feature: TransformERC20Feature as ContractArtifact, UniswapFeature: UniswapFeature as ContractArtifact, IBatchFillNativeOrdersFeature: IBatchFillNativeOrdersFeature as ContractArtifact, @@ -197,6 +190,7 @@ export const artifacts = { INativeOrdersEvents: INativeOrdersEvents as ContractArtifact, INativeOrdersFeature: INativeOrdersFeature as ContractArtifact, IOwnableFeature: IOwnableFeature as ContractArtifact, + IPancakeSwapFeature: IPancakeSwapFeature as ContractArtifact, ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact, ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact, ITransformERC20Feature: ITransformERC20Feature as ContractArtifact, @@ -225,7 +219,6 @@ export const artifacts = { LibReentrancyGuardStorage: LibReentrancyGuardStorage as ContractArtifact, LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact, LibStorage: LibStorage as ContractArtifact, - LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact, LibTransformERC20Storage: LibTransformERC20Storage as ContractArtifact, AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact, FillQuoteTransformer: FillQuoteTransformer as ContractArtifact, @@ -237,7 +230,7 @@ export const artifacts = { Transformer: Transformer as ContractArtifact, WethTransformer: WethTransformer as ContractArtifact, BridgeAdapter: BridgeAdapter as ContractArtifact, - BridgeSource: BridgeSource as ContractArtifact, + BridgeProtocols: BridgeProtocols as ContractArtifact, IBridgeAdapter: IBridgeAdapter as ContractArtifact, MixinBalancer: MixinBalancer as ContractArtifact, MixinBancor: MixinBancor as ContractArtifact, @@ -249,9 +242,9 @@ export const artifacts = { MixinKyber: MixinKyber as ContractArtifact, MixinMStable: MixinMStable as ContractArtifact, MixinMooniswap: MixinMooniswap as ContractArtifact, + MixinNerve: MixinNerve as ContractArtifact, MixinOasis: MixinOasis as ContractArtifact, MixinShell: MixinShell as ContractArtifact, - MixinSushiswap: MixinSushiswap as ContractArtifact, MixinUniswap: MixinUniswap as ContractArtifact, MixinUniswapV2: MixinUniswapV2 as ContractArtifact, MixinZeroExBridge: MixinZeroExBridge as ContractArtifact, @@ -289,7 +282,6 @@ export const artifacts = { TestSimpleFunctionRegistryFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1 as ContractArtifact, TestSimpleFunctionRegistryFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2 as ContractArtifact, TestStaking: TestStaking as ContractArtifact, - TestTokenSpender: TestTokenSpender as ContractArtifact, TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact, TestTransformERC20: TestTransformERC20 as ContractArtifact, TestTransformerBase: TestTransformerBase as ContractArtifact, diff --git a/contracts/zero-ex/test/features/liquidity_provider_test.ts b/contracts/zero-ex/test/features/liquidity_provider_test.ts index de5647cf1c..e312567103 100644 --- a/contracts/zero-ex/test/features/liquidity_provider_test.ts +++ b/contracts/zero-ex/test/features/liquidity_provider_test.ts @@ -2,7 +2,6 @@ import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contra import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils'; import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils'; -import { ZERO_BYTES32 } from '../../src/constants'; import { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers'; import { artifacts } from '../artifacts'; import { abis } from '../utils/abis'; @@ -63,7 +62,6 @@ blockchainTests('LiquidityProvider feature', env => { env.txDefaults, artifacts, sandbox.address, - ZERO_BYTES32, ); await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis) .migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner) diff --git a/contracts/zero-ex/test/features/multiplex_test.ts b/contracts/zero-ex/test/features/multiplex_test.ts index 3df817a8d4..4e5a2ca3db 100644 --- a/contracts/zero-ex/test/features/multiplex_test.ts +++ b/contracts/zero-ex/test/features/multiplex_test.ts @@ -8,7 +8,8 @@ import { } from '@0x/contracts-erc20'; import { blockchainTests, constants, expect, filterLogsToArguments, toBaseUnitAmount } from '@0x/contracts-test-utils'; import { - BridgeSource, + BridgeProtocol, + encodeBridgeSourceId, encodeFillQuoteTransformerData, encodePayTakerTransformerData, FillQuoteTransformerOrderType, @@ -72,8 +73,9 @@ blockchainTests.fork.skip('Multiplex feature', env => { const WETH_DAI_PLP_ADDRESS = '0x1db681925786441ba82adefac7bf492089665ca0'; const WETH_USDC_PLP_ADDRESS = '0x8463c03c0c57ff19fa8b431e0d3a34e2df89888e'; const USDC_USDT_PLP_ADDRESS = '0xc340ef96449514cea4dfa11d847a06d7f03d437c'; - const GREEDY_TOKENS_BLOOM_FILTER = '0x0000100800000480002c00401000000820000000000000020000001010800001'; const BALANCER_WETH_DAI = '0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a'; + const CURVE_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve'); + const BALANCER_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Bancor, 'Balancer'); const fqtNonce = findTransformerNonce( '0xfa6282736af206cb4cfc5cb786d82aecdf1186f9', '0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb', @@ -112,7 +114,6 @@ blockchainTests.fork.skip('Multiplex feature', env => { zeroEx.address, WETH_ADDRESS, PLP_SANDBOX_ADDRESS, - GREEDY_TOKENS_BLOOM_FILTER, ); await registry .extend(multiplex.getSelector('batchFill'), multiplexImpl.address) @@ -283,7 +284,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { buyToken: WETH_ADDRESS, bridgeOrders: [ { - source: BridgeSource.Balancer, + source: BALANCER_BRIDGE_SOURCE_ID, takerTokenAmount: expiredRfqCall.sellAmount, makerTokenAmount: expiredRfqCall.sellAmount, bridgeData: poolEncoder.encode([BALANCER_WETH_DAI]), @@ -339,7 +340,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { tx.logs, BridgeAdapterEvents.BridgeFill, ); - expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Balancer); + expect(bridgeFillEvent.source).to.bignumber.equal(BALANCER_BRIDGE_SOURCE_ID); expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS); expect(bridgeFillEvent.outputToken).to.equal(WETH_ADDRESS); expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(expiredRfqCall.sellAmount); @@ -472,7 +473,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { buyToken: USDC_ADDRESS, bridgeOrders: [ { - source: BridgeSource.Curve, + source: CURVE_BRIDGE_SOURCE_ID, takerTokenAmount: sellAmount, makerTokenAmount: sellAmount, bridgeData: curveEncoder.encode([ @@ -531,7 +532,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { tx.logs, BridgeAdapterEvents.BridgeFill, ); - expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve); + expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID); expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS); expect(bridgeFillEvent.outputToken).to.equal(USDC_ADDRESS); expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(sellAmount); @@ -563,7 +564,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { buyToken: DAI_ADDRESS, bridgeOrders: [ { - source: BridgeSource.Curve, + source: CURVE_BRIDGE_SOURCE_ID, takerTokenAmount: constants.MAX_UINT256, makerTokenAmount: constants.MAX_UINT256, bridgeData: curveEncoder.encode([ @@ -640,7 +641,7 @@ blockchainTests.fork.skip('Multiplex feature', env => { tx.logs, BridgeAdapterEvents.BridgeFill, ); - expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve); + expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID); expect(bridgeFillEvent.inputToken).to.equal(USDC_ADDRESS); expect(bridgeFillEvent.outputToken).to.equal(DAI_ADDRESS); expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(uniswapOutputAmount); diff --git a/contracts/zero-ex/test/features/token_spender_test.ts b/contracts/zero-ex/test/features/token_spender_test.ts deleted file mode 100644 index 0a1d24b8b1..0000000000 --- a/contracts/zero-ex/test/features/token_spender_test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - blockchainTests, - expect, - getRandomInteger, - randomAddress, - verifyEventsFromLogs, -} from '@0x/contracts-test-utils'; -import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils'; - -import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers'; -import { artifacts } from '../artifacts'; -import { abis } from '../utils/abis'; -import { fullMigrateAsync } from '../utils/migration'; -import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers'; - -blockchainTests.resets('TokenSpender feature', env => { - let zeroEx: IZeroExContract; - let feature: TokenSpenderFeatureContract; - let token: TestTokenSpenderERC20TokenContract; - let allowanceTarget: string; - - before(async () => { - const [owner] = await env.getAccountAddressesAsync(); - zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, { - tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync( - artifacts.TestTokenSpender, - env.provider, - env.txDefaults, - artifacts, - )).address, - }); - feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis); - token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.TestTokenSpenderERC20Token, - env.provider, - env.txDefaults, - artifacts, - ); - allowanceTarget = await feature.getAllowanceTarget().callAsync(); - }); - - describe('_spendERC20Tokens()', () => { - const EMPTY_RETURN_AMOUNT = 1337; - const FALSE_RETURN_AMOUNT = 1338; - const REVERT_RETURN_AMOUNT = 1339; - - it('_spendERC20Tokens() successfully calls compliant ERC20 token', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(123456); - const receipt = await feature - ._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - verifyEventsFromLogs( - receipt.logs, - [ - { - sender: allowanceTarget, - from: tokenFrom, - to: tokenTo, - amount: tokenAmount, - }, - ], - TestTokenSpenderERC20TokenEvents.TransferFromCalled, - ); - }); - - it('_spendERC20Tokens() successfully calls non-compliant ERC20 token', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(EMPTY_RETURN_AMOUNT); - const receipt = await feature - ._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - verifyEventsFromLogs( - receipt.logs, - [ - { - sender: allowanceTarget, - from: tokenFrom, - to: tokenTo, - amount: tokenAmount, - }, - ], - TestTokenSpenderERC20TokenEvents.TransferFromCalled, - ); - }); - - it('_spendERC20Tokens() reverts if ERC20 token reverts', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(REVERT_RETURN_AMOUNT); - const tx = feature - ._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError( - token.address, - tokenFrom, - tokenTo, - tokenAmount, - new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(), - ); - return expect(tx).to.revertWith(expectedError); - }); - - it('_spendERC20Tokens() reverts if ERC20 token returns false', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(FALSE_RETURN_AMOUNT); - const tx = feature - ._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - return expect(tx).to.revertWith( - new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError( - token.address, - tokenFrom, - tokenTo, - tokenAmount, - hexUtils.leftPad(0), - ), - ); - }); - }); - - describe('getSpendableERC20BalanceOf()', () => { - it("returns the minimum of the owner's balance and allowance", async () => { - const balance = getRandomInteger(1, '1e18'); - const allowance = getRandomInteger(1, '1e18'); - const tokenOwner = randomAddress(); - await token - .setBalanceAndAllowanceOf(tokenOwner, balance, allowanceTarget, allowance) - .awaitTransactionSuccessAsync(); - const spendableBalance = await feature.getSpendableERC20BalanceOf(token.address, tokenOwner).callAsync(); - expect(spendableBalance).to.bignumber.eq(BigNumber.min(balance, allowance)); - }); - }); -}); diff --git a/contracts/zero-ex/test/fixin_token_spender_test.ts b/contracts/zero-ex/test/fixin_token_spender_test.ts index c504803e87..c24a3a4462 100644 --- a/contracts/zero-ex/test/fixin_token_spender_test.ts +++ b/contracts/zero-ex/test/fixin_token_spender_test.ts @@ -5,14 +5,11 @@ import { randomAddress, verifyEventsFromLogs, } from '@0x/contracts-test-utils'; -import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils'; - -import { getTokenListBloomFilter } from '../src/bloom_filter_utils'; +import { BigNumber, hexUtils, RawRevertError, StringRevertError } from '@0x/utils'; import { artifacts } from './artifacts'; import { TestFixinTokenSpenderContract, - TestFixinTokenSpenderEvents, TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents, } from './wrappers'; @@ -21,7 +18,6 @@ blockchainTests.resets('FixinTokenSpender', env => { let tokenSpender: TestFixinTokenSpenderContract; let token: TestTokenSpenderERC20TokenContract; let greedyToken: TestTokenSpenderERC20TokenContract; - let greedyTokensBloomFilter: string; before(async () => { token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync( @@ -38,14 +34,11 @@ blockchainTests.resets('FixinTokenSpender', env => { ); await greedyToken.setGreedyRevert(true).awaitTransactionSuccessAsync(); - greedyTokensBloomFilter = getTokenListBloomFilter([greedyToken.address]); - tokenSpender = await TestFixinTokenSpenderContract.deployFrom0xArtifactAsync( artifacts.TestFixinTokenSpender, env.provider, env.txDefaults, artifacts, - greedyTokensBloomFilter, ); }); @@ -53,7 +46,6 @@ blockchainTests.resets('FixinTokenSpender', env => { const EMPTY_RETURN_AMOUNT = 1337; const FALSE_RETURN_AMOUNT = 1338; const REVERT_RETURN_AMOUNT = 1339; - const TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340; const EXTRA_RETURN_TRUE_AMOUNT = 1341; const EXTRA_RETURN_FALSE_AMOUNT = 1342; @@ -106,13 +98,7 @@ blockchainTests.resets('FixinTokenSpender', env => { const tx = tokenSpender .transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) .awaitTransactionSuccessAsync(); - const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError( - token.address, - tokenFrom, - tokenTo, - tokenAmount, - new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(), - ); + const expectedError = new StringRevertError('TestTokenSpenderERC20Token/Revert'); return expect(tx).to.revertWith(expectedError); }); @@ -123,36 +109,7 @@ blockchainTests.resets('FixinTokenSpender', env => { const tx = tokenSpender .transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) .awaitTransactionSuccessAsync(); - return expect(tx).to.revertWith( - new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError( - token.address, - tokenFrom, - tokenTo, - tokenAmount, - hexUtils.leftPad(0), - ), - ); - }); - - it('transferERC20Tokens() falls back successfully to TokenSpender._spendERC20Tokens()', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT); - const receipt = await tokenSpender - .transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - verifyEventsFromLogs( - receipt.logs, - [ - { - token: token.address, - owner: tokenFrom, - to: tokenTo, - amount: tokenAmount, - }, - ], - TestFixinTokenSpenderEvents.FallbackCalled, - ); + return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0))); }); it('transferERC20Tokens() allows extra data after true', async () => { @@ -185,15 +142,7 @@ blockchainTests.resets('FixinTokenSpender', env => { const tx = tokenSpender .transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount) .awaitTransactionSuccessAsync(); - return expect(tx).to.revertWith( - new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError( - token.address, - tokenFrom, - tokenTo, - tokenAmount, - hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64), - ), - ); + return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64))); }); it('transferERC20Tokens() cannot call self', async () => { @@ -206,73 +155,6 @@ blockchainTests.resets('FixinTokenSpender', env => { .awaitTransactionSuccessAsync(); return expect(tx).to.revertWith('FixinTokenSpender/CANNOT_INVOKE_SELF'); }); - - it('calls transferFrom() directly for a greedy token with allowance', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(123456); - await greedyToken.approveAs(tokenFrom, tokenSpender.address, tokenAmount).awaitTransactionSuccessAsync(); - - const receipt = await tokenSpender - .transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - // Because there is an allowance set, we will call transferFrom() - // directly, which succeds and emits an event. - verifyEventsFromLogs( - receipt.logs, - [ - { - sender: tokenSpender.address, - from: tokenFrom, - to: tokenTo, - amount: tokenAmount, - }, - ], // No events. - TestTokenSpenderERC20TokenEvents.TransferFromCalled, - ); - }); - - it('calls only the fallback for a greedy token with no allowance', async () => { - const tokenFrom = randomAddress(); - const tokenTo = randomAddress(); - const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT); - - const receipt = await tokenSpender - .transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount) - .awaitTransactionSuccessAsync(); - // Because this is a greedy token and there is no allowance set, transferFrom() - // will not be called before hitting the fallback, which only emits an event - // in the test contract. - verifyEventsFromLogs( - receipt.logs, - [], // No events. - TestTokenSpenderERC20TokenEvents.TransferFromCalled, - ); - verifyEventsFromLogs( - receipt.logs, - [ - { - token: greedyToken.address, - owner: tokenFrom, - to: tokenTo, - amount: tokenAmount, - }, - ], - TestFixinTokenSpenderEvents.FallbackCalled, - ); - }); - }); - - describe('isTokenPossiblyGreedy()', () => { - it('returns true for greedy token', async () => { - const isGreedy = await tokenSpender.isTokenPossiblyGreedy(greedyToken.address).callAsync(); - expect(isGreedy).to.eq(true); - }); - - it('returns false for non-greedy token', async () => { - const isGreedy = await tokenSpender.isTokenPossiblyGreedy(token.address).callAsync(); - expect(isGreedy).to.eq(false); - }); }); describe('getSpendableERC20BalanceOf()', () => { diff --git a/contracts/zero-ex/test/full_migration_test.ts b/contracts/zero-ex/test/full_migration_test.ts index 2ab5506c73..6177247450 100644 --- a/contracts/zero-ex/test/full_migration_test.ts +++ b/contracts/zero-ex/test/full_migration_test.ts @@ -8,11 +8,9 @@ import { artifacts } from './artifacts'; import { abis } from './utils/abis'; import { deployFullFeaturesAsync, FullFeatures } from './utils/migration'; import { - AllowanceTargetContract, IMetaTransactionsFeatureContract, INativeOrdersFeatureContract, IOwnableFeatureContract, - ITokenSpenderFeatureContract, ITransformERC20FeatureContract, TestFullMigrationContract, ZeroExContract, @@ -69,10 +67,6 @@ blockchainTests.resets('Full migration', env => { }); const FEATURE_FNS = { - TokenSpender: { - contractType: ITokenSpenderFeatureContract, - fns: ['_spendERC20Tokens'], - }, TransformERC20: { contractType: ITransformERC20FeatureContract, fns: [ @@ -207,27 +201,6 @@ blockchainTests.resets('Full migration', env => { }); } - describe("TokenSpender's allowance target", () => { - let allowanceTarget: AllowanceTargetContract; - - before(async () => { - const contract = new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults); - allowanceTarget = new AllowanceTargetContract( - await contract.getAllowanceTarget().callAsync(), - env.provider, - env.txDefaults, - ); - }); - - it('is owned by owner', async () => { - return expect(allowanceTarget.owner().callAsync()).to.become(owner); - }); - - it('Proxy is authorized', async () => { - return expect(allowanceTarget.authorized(zeroEx.address).callAsync()).to.become(true); - }); - }); - describe('TransformERC20', () => { let feature: ITransformERC20FeatureContract; diff --git a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts index 1d0e2ec4de..4485dbb941 100644 --- a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts +++ b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts @@ -52,7 +52,7 @@ blockchainTests.resets('FillQuoteTransformer', env => { let singleProtocolFee: BigNumber; const GAS_PRICE = 1337; - const TEST_BRIDGE_SOURCE = 12345678; + const TEST_BRIDGE_SOURCE = hexUtils.random(32); const HIGH_BIT = new BigNumber(2).pow(255); const REVERT_AMOUNT = new BigNumber('0xdeadbeef'); diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index 3c84d1d708..60bfeca254 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -4,11 +4,10 @@ * ----------------------------------------------------------------------------- */ export * from '../test/generated-wrappers/affiliate_fee_transformer'; -export * from '../test/generated-wrappers/allowance_target'; export * from '../test/generated-wrappers/batch_fill_native_orders_feature'; export * from '../test/generated-wrappers/bootstrap_feature'; export * from '../test/generated-wrappers/bridge_adapter'; -export * from '../test/generated-wrappers/bridge_source'; +export * from '../test/generated-wrappers/bridge_protocols'; export * from '../test/generated-wrappers/curve_liquidity_provider'; export * from '../test/generated-wrappers/fee_collector'; export * from '../test/generated-wrappers/fee_collector_controller'; @@ -20,7 +19,6 @@ export * from '../test/generated-wrappers/fixin_reentrancy_guard'; export * from '../test/generated-wrappers/fixin_token_spender'; export * from '../test/generated-wrappers/flash_wallet'; export * from '../test/generated-wrappers/full_migration'; -export * from '../test/generated-wrappers/i_allowance_target'; export * from '../test/generated-wrappers/i_batch_fill_native_orders_feature'; export * from '../test/generated-wrappers/i_bootstrap_feature'; export * from '../test/generated-wrappers/i_bridge_adapter'; @@ -37,6 +35,7 @@ export * from '../test/generated-wrappers/i_multiplex_feature'; export * from '../test/generated-wrappers/i_native_orders_events'; export * from '../test/generated-wrappers/i_native_orders_feature'; export * from '../test/generated-wrappers/i_ownable_feature'; +export * from '../test/generated-wrappers/i_pancake_swap_feature'; export * from '../test/generated-wrappers/i_simple_function_registry_feature'; export * from '../test/generated-wrappers/i_staking'; export * from '../test/generated-wrappers/i_test_simple_function_registry_feature'; @@ -66,9 +65,7 @@ export * from '../test/generated-wrappers/lib_signature'; export * from '../test/generated-wrappers/lib_signature_rich_errors'; export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors'; export * from '../test/generated-wrappers/lib_simple_function_registry_storage'; -export * from '../test/generated-wrappers/lib_spender_rich_errors'; export * from '../test/generated-wrappers/lib_storage'; -export * from '../test/generated-wrappers/lib_token_spender_storage'; export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors'; export * from '../test/generated-wrappers/lib_transform_erc20_storage'; export * from '../test/generated-wrappers/lib_wallet_rich_errors'; @@ -86,9 +83,9 @@ export * from '../test/generated-wrappers/mixin_dodo_v2'; export * from '../test/generated-wrappers/mixin_kyber'; export * from '../test/generated-wrappers/mixin_m_stable'; export * from '../test/generated-wrappers/mixin_mooniswap'; +export * from '../test/generated-wrappers/mixin_nerve'; export * from '../test/generated-wrappers/mixin_oasis'; export * from '../test/generated-wrappers/mixin_shell'; -export * from '../test/generated-wrappers/mixin_sushiswap'; export * from '../test/generated-wrappers/mixin_uniswap'; export * from '../test/generated-wrappers/mixin_uniswap_v2'; export * from '../test/generated-wrappers/mixin_zero_ex_bridge'; @@ -100,6 +97,7 @@ export * from '../test/generated-wrappers/native_orders_info'; export * from '../test/generated-wrappers/native_orders_protocol_fees'; export * from '../test/generated-wrappers/native_orders_settlement'; export * from '../test/generated-wrappers/ownable_feature'; +export * from '../test/generated-wrappers/pancake_swap_feature'; export * from '../test/generated-wrappers/pay_taker_transformer'; export * from '../test/generated-wrappers/permissionless_transformer_deployer'; export * from '../test/generated-wrappers/positive_slippage_fee_transformer'; @@ -132,7 +130,6 @@ export * from '../test/generated-wrappers/test_rfq_origin_registration'; export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl1'; export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl2'; export * from '../test/generated-wrappers/test_staking'; -export * from '../test/generated-wrappers/test_token_spender'; export * from '../test/generated-wrappers/test_token_spender_erc20_token'; export * from '../test/generated-wrappers/test_transform_erc20'; export * from '../test/generated-wrappers/test_transformer_base'; @@ -141,7 +138,6 @@ export * from '../test/generated-wrappers/test_transformer_host'; export * from '../test/generated-wrappers/test_weth'; export * from '../test/generated-wrappers/test_weth_transformer_host'; export * from '../test/generated-wrappers/test_zero_ex_feature'; -export * from '../test/generated-wrappers/token_spender_feature'; export * from '../test/generated-wrappers/transform_erc20_feature'; export * from '../test/generated-wrappers/transformer'; export * from '../test/generated-wrappers/transformer_deployer'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index b0bafb8853..14a743f46e 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -20,7 +20,6 @@ "generated-artifacts/INativeOrdersFeature.json", "generated-artifacts/IOwnableFeature.json", "generated-artifacts/ISimpleFunctionRegistryFeature.json", - "generated-artifacts/ITokenSpenderFeature.json", "generated-artifacts/ITransformERC20Feature.json", "generated-artifacts/IZeroEx.json", "generated-artifacts/InitialMigration.json", @@ -33,16 +32,14 @@ "generated-artifacts/PayTakerTransformer.json", "generated-artifacts/PositiveSlippageFeeTransformer.json", "generated-artifacts/SimpleFunctionRegistryFeature.json", - "generated-artifacts/TokenSpenderFeature.json", "generated-artifacts/TransformERC20Feature.json", "generated-artifacts/WethTransformer.json", "generated-artifacts/ZeroEx.json", "test/generated-artifacts/AffiliateFeeTransformer.json", - "test/generated-artifacts/AllowanceTarget.json", "test/generated-artifacts/BatchFillNativeOrdersFeature.json", "test/generated-artifacts/BootstrapFeature.json", "test/generated-artifacts/BridgeAdapter.json", - "test/generated-artifacts/BridgeSource.json", + "test/generated-artifacts/BridgeProtocols.json", "test/generated-artifacts/CurveLiquidityProvider.json", "test/generated-artifacts/FeeCollector.json", "test/generated-artifacts/FeeCollectorController.json", @@ -54,7 +51,6 @@ "test/generated-artifacts/FixinTokenSpender.json", "test/generated-artifacts/FlashWallet.json", "test/generated-artifacts/FullMigration.json", - "test/generated-artifacts/IAllowanceTarget.json", "test/generated-artifacts/IBatchFillNativeOrdersFeature.json", "test/generated-artifacts/IBootstrapFeature.json", "test/generated-artifacts/IBridgeAdapter.json", @@ -71,6 +67,7 @@ "test/generated-artifacts/INativeOrdersEvents.json", "test/generated-artifacts/INativeOrdersFeature.json", "test/generated-artifacts/IOwnableFeature.json", + "test/generated-artifacts/IPancakeSwapFeature.json", "test/generated-artifacts/ISimpleFunctionRegistryFeature.json", "test/generated-artifacts/IStaking.json", "test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json", @@ -100,9 +97,7 @@ "test/generated-artifacts/LibSignatureRichErrors.json", "test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json", "test/generated-artifacts/LibSimpleFunctionRegistryStorage.json", - "test/generated-artifacts/LibSpenderRichErrors.json", "test/generated-artifacts/LibStorage.json", - "test/generated-artifacts/LibTokenSpenderStorage.json", "test/generated-artifacts/LibTransformERC20RichErrors.json", "test/generated-artifacts/LibTransformERC20Storage.json", "test/generated-artifacts/LibWalletRichErrors.json", @@ -120,9 +115,9 @@ "test/generated-artifacts/MixinKyber.json", "test/generated-artifacts/MixinMStable.json", "test/generated-artifacts/MixinMooniswap.json", + "test/generated-artifacts/MixinNerve.json", "test/generated-artifacts/MixinOasis.json", "test/generated-artifacts/MixinShell.json", - "test/generated-artifacts/MixinSushiswap.json", "test/generated-artifacts/MixinUniswap.json", "test/generated-artifacts/MixinUniswapV2.json", "test/generated-artifacts/MixinZeroExBridge.json", @@ -134,6 +129,7 @@ "test/generated-artifacts/NativeOrdersProtocolFees.json", "test/generated-artifacts/NativeOrdersSettlement.json", "test/generated-artifacts/OwnableFeature.json", + "test/generated-artifacts/PancakeSwapFeature.json", "test/generated-artifacts/PayTakerTransformer.json", "test/generated-artifacts/PermissionlessTransformerDeployer.json", "test/generated-artifacts/PositiveSlippageFeeTransformer.json", @@ -166,7 +162,6 @@ "test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json", "test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json", "test/generated-artifacts/TestStaking.json", - "test/generated-artifacts/TestTokenSpender.json", "test/generated-artifacts/TestTokenSpenderERC20Token.json", "test/generated-artifacts/TestTransformERC20.json", "test/generated-artifacts/TestTransformerBase.json", @@ -175,7 +170,6 @@ "test/generated-artifacts/TestWeth.json", "test/generated-artifacts/TestWethTransformerHost.json", "test/generated-artifacts/TestZeroExFeature.json", - "test/generated-artifacts/TokenSpenderFeature.json", "test/generated-artifacts/TransformERC20Feature.json", "test/generated-artifacts/Transformer.json", "test/generated-artifacts/TransformerDeployer.json", diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index a7c3fc832b..1b94c473fd 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -17,6 +17,22 @@ { "note": "improve logging for alt RFQ requests", "pr": 158 + }, + { + "note": "Use new bridge source ID encoding.", + "pr": 162 + }, + { + "note": "Refactor to provide chain id specific addresses", + "pr": 163 + }, + { + "note": "Added PancakeSwap and BakerySwap on Chain 56", + "pr": 163 + }, + { + "note": "Added Nerve and Dodo (v1) to BSC", + "pr": 181 } ] }, diff --git a/packages/asset-swapper/contracts/src/BalancerSampler.sol b/packages/asset-swapper/contracts/src/BalancerSampler.sol index e35385357d..019b9e98ec 100644 --- a/packages/asset-swapper/contracts/src/BalancerSampler.sol +++ b/packages/asset-swapper/contracts/src/BalancerSampler.sol @@ -93,6 +93,10 @@ contract BalancerSampler { returns (uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; @@ -151,6 +155,10 @@ contract BalancerSampler { returns (uint256 amount) { takerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (takerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; diff --git a/packages/asset-swapper/contracts/src/BancorSampler.sol b/packages/asset-swapper/contracts/src/BancorSampler.sol index 4a861163ff..3ef40cbe8e 100644 --- a/packages/asset-swapper/contracts/src/BancorSampler.sol +++ b/packages/asset-swapper/contracts/src/BancorSampler.sol @@ -20,21 +20,23 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IBancor.sol"; +contract DeploymentConstants {} - -contract BancorSampler is - DeploymentConstants +contract BancorSampler is DeploymentConstants { /// @dev Base gas limit for Bancor calls. uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k - address constant private BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + struct BancorSamplerOpts { + address registry; + address[][] paths; + } /// @dev Sample sell quotes from Bancor. - /// @param paths The paths to check for Bancor. Only the best is used + /// @param opts BancorSamplerOpts The Bancor registry contract address and paths /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. @@ -43,7 +45,7 @@ contract BancorSampler is /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromBancor( - address[][] memory paths, + BancorSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -52,38 +54,13 @@ contract BancorSampler is view returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts) { - bancorNetwork = _getBancorNetwork(); - if (paths.length == 0) { + if (opts.paths.length == 0) { return (bancorNetwork, path, makerTokenAmounts); } - uint256 maxBoughtAmount = 0; - // Find the best path by selling the largest taker amount - for (uint256 i = 0; i < paths.length; i++) { - if (paths[i].length < 2) { - continue; - } + (bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts); + makerTokenAmounts = new uint256[](takerTokenAmounts.length); - try - IBancorNetwork(bancorNetwork) - .rateByPath - {gas: BANCOR_CALL_GAS} - (paths[i], takerTokenAmounts[takerTokenAmounts.length-1]) - returns (uint256 amount) - { - if (amount > maxBoughtAmount) { - maxBoughtAmount = amount; - path = paths[i]; - } - } catch { - // Swallow failures, leaving all results as zero. - continue; - } - } - - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - - for (uint256 i = 0; i < numSamples; i++) { + for (uint256 i = 0; i < makerTokenAmounts.length; i++) { try IBancorNetwork(bancorNetwork) .rateByPath @@ -92,6 +69,10 @@ contract BancorSampler is returns (uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch { // Swallow failures, leaving all results as zero. break; @@ -101,7 +82,7 @@ contract BancorSampler is } /// @dev Sample buy quotes from Bancor. Unimplemented - /// @param paths The paths to check for Bancor. Only the best is used + /// @param opts BancorSamplerOpts The Bancor registry contract address and paths /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. @@ -110,7 +91,7 @@ contract BancorSampler is /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromBancor( - address[][] memory paths, + BancorSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -121,12 +102,51 @@ contract BancorSampler is { } - function _getBancorNetwork() + function _findBestPath( + BancorSamplerOpts memory opts, + address takerToken, + address makerToken, + uint256[] memory takerTokenAmounts + ) + internal + view + returns (address bancorNetwork, address[] memory path) + { + bancorNetwork = _getBancorNetwork(opts.registry); + if (opts.paths.length == 0) { + return (bancorNetwork, path); + } + uint256 maxBoughtAmount = 0; + // Find the best path by selling the largest taker amount + for (uint256 i = 0; i < opts.paths.length; i++) { + if (opts.paths[i].length < 2) { + continue; + } + + try + IBancorNetwork(bancorNetwork) + .rateByPath + {gas: BANCOR_CALL_GAS} + (opts.paths[i], takerTokenAmounts[takerTokenAmounts.length-1]) + returns (uint256 amount) + { + if (amount > maxBoughtAmount) { + maxBoughtAmount = amount; + path = opts.paths[i]; + } + } catch { + // Swallow failures, leaving all results as zero. + continue; + } + } + } + + function _getBancorNetwork(address registry) private view returns (address) { - IBancorRegistry registry = IBancorRegistry(_getBancorRegistryAddress()); + IBancorRegistry registry = IBancorRegistry(registry); return registry.getAddress(registry.BANCOR_NETWORK()); } } diff --git a/packages/asset-swapper/contracts/src/CurveSampler.sol b/packages/asset-swapper/contracts/src/CurveSampler.sol index 15f394a04b..d2f743d9db 100644 --- a/packages/asset-swapper/contracts/src/CurveSampler.sol +++ b/packages/asset-swapper/contracts/src/CurveSampler.sol @@ -72,11 +72,11 @@ contract CurveSampler is if (didSucceed) { buyAmount = abi.decode(resultData, (uint256)); } - // Exit early if the amount is too high for the source to serve - if (buyAmount == 0) { + makerTokenAmounts[i] = buyAmount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { break; } - makerTokenAmounts[i] = buyAmount; } } @@ -123,11 +123,11 @@ contract CurveSampler is if (didSucceed) { sellAmount = abi.decode(resultData, (uint256)); } - // Exit early if the amount is too high for the source to serve - if (sellAmount == 0) { + takerTokenAmounts[i] = sellAmount; + // Break early if there are 0 amounts + if (takerTokenAmounts[i] == 0) { break; } - takerTokenAmounts[i] = sellAmount; } } diff --git a/packages/asset-swapper/contracts/src/DODOSampler.sol b/packages/asset-swapper/contracts/src/DODOSampler.sol index de238d66ce..49e8ecf66b 100644 --- a/packages/asset-swapper/contracts/src/DODOSampler.sol +++ b/packages/asset-swapper/contracts/src/DODOSampler.sol @@ -20,7 +20,6 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; @@ -39,15 +38,19 @@ interface IDODO { } contract DODOSampler is - DeploymentConstants, SamplerUtils, ApproximateBuys { /// @dev Gas limit for DODO calls. uint256 constant private DODO_CALL_GAS = 300e3; // 300k + struct DODOSamplerOpts { + address registry; + address helper; + } /// @dev Sample sell quotes from DODO. + /// @param opts DODOSamplerOpts DODO Registry and helper addresses /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. @@ -56,6 +59,7 @@ contract DODOSampler is /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromDODO( + DODOSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -68,14 +72,14 @@ contract DODOSampler is uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); - pool = IDODOZoo(_getDODORegistryAddress()).getDODO(takerToken, makerToken); + pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken); address baseToken; // If pool exists we have the correct order of Base/Quote if (pool != address(0)) { baseToken = takerToken; sellBase = true; } else { - pool = IDODOZoo(_getDODORegistryAddress()).getDODO(makerToken, takerToken); + pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken); // No pool either direction if (address(pool) == address(0)) { return (sellBase, pool, makerTokenAmounts); @@ -91,19 +95,20 @@ contract DODOSampler is for (uint256 i = 0; i < numSamples; i++) { uint256 buyAmount = _sampleSellForApproximateBuyFromDODO( - abi.encode(takerToken, pool, baseToken), // taker token data - abi.encode(makerToken, pool, baseToken), // maker token data + abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data + abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data takerTokenAmounts[i] ); - // Exit early if the amount is too high for the source to serve - if (buyAmount == 0) { + makerTokenAmounts[i] = buyAmount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { break; } - makerTokenAmounts[i] = buyAmount; } } /// @dev Sample buy quotes from DODO. + /// @param opts DODOSamplerOpts DODO Registry and helper addresses /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token sell amount for each sample. @@ -112,6 +117,7 @@ contract DODOSampler is /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromDODO( + DODOSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -126,7 +132,7 @@ contract DODOSampler is // Pool is BASE/QUOTE // Look up the pool from the taker/maker combination - pool = IDODOZoo(_getDODORegistryAddress()).getDODO(takerToken, makerToken); + pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken); address baseToken; // If pool exists we have the correct order of Base/Quote if (pool != address(0)) { @@ -134,7 +140,7 @@ contract DODOSampler is sellBase = true; } else { // Look up the pool from the maker/taker combination - pool = IDODOZoo(_getDODORegistryAddress()).getDODO(makerToken, takerToken); + pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken); // No pool either direction if (address(pool) == address(0)) { return (sellBase, pool, takerTokenAmounts); @@ -150,8 +156,8 @@ contract DODOSampler is takerTokenAmounts = _sampleApproximateBuys( ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, pool, baseToken), - takerTokenData: abi.encode(takerToken, pool, baseToken), + makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper), + takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper), getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO }), makerTokenAmounts @@ -167,9 +173,9 @@ contract DODOSampler is view returns (uint256) { - (address takerToken, address pool, address baseToken) = abi.decode( + (address takerToken, address pool, address baseToken, address helper) = abi.decode( takerTokenData, - (address, address, address) + (address, address, address, address) ); // We will get called to sell both the taker token and also to sell the maker token @@ -189,7 +195,7 @@ contract DODOSampler is } else { // If quote token then use helper, this is less accurate try - IDODOHelper(_getDODOHelperAddress()).querySellQuoteToken + IDODOHelper(helper).querySellQuoteToken {gas: DODO_CALL_GAS} (pool, sellAmount) returns (uint256 amount) diff --git a/packages/asset-swapper/contracts/src/DODOV2Sampler.sol b/packages/asset-swapper/contracts/src/DODOV2Sampler.sol index eea98364e4..6b99135cc9 100644 --- a/packages/asset-swapper/contracts/src/DODOV2Sampler.sol +++ b/packages/asset-swapper/contracts/src/DODOV2Sampler.sol @@ -20,7 +20,6 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; @@ -44,7 +43,6 @@ interface IDODOV2Pool { } contract DODOV2Sampler is - DeploymentConstants, SamplerUtils, ApproximateBuys { @@ -89,11 +87,11 @@ contract DODOV2Sampler is abi.encode(makerToken, pool, sellBase), // maker token data takerTokenAmounts[i] ); - // Exit early if the amount is too high for the source to serve - if (buyAmount == 0) { + makerTokenAmounts[i] = buyAmount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { break; } - makerTokenAmounts[i] = buyAmount; } } diff --git a/packages/asset-swapper/contracts/src/DeploymentConstants.sol b/packages/asset-swapper/contracts/src/DeploymentConstants.sol deleted file mode 100644 index 5bf783b45a..0000000000 --- a/packages/asset-swapper/contracts/src/DeploymentConstants.sol +++ /dev/null @@ -1,353 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6; - - -contract DeploymentConstants { - - // solhint-disable separate-by-one-line-in-contract - - // Mainnet addresses /////////////////////////////////////////////////////// - /// @dev Mainnet address of the WETH contract. - address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - /// @dev Mainnet address of the KyberNetworkProxy contract. - address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x9AAb3f75489902f3a48495025729a0AF77d4b11e; - /// @dev Mainnet address of the KyberHintHandler contract. - address constant private KYBER_HINT_HANDLER_ADDRESS = 0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C; - /// @dev Mainnet address of the `UniswapExchangeFactory` contract. - address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; - /// @dev Mainnet address of the `UniswapV2Router01` contract. - address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a; - /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract. - address constant private ETH2DAI_ADDRESS = 0x794e6e91555438aFc3ccF1c5076A74F42133d08D; - /// @dev Mainnet address of the `ERC20BridgeProxy` contract - address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x8ED95d1746bf1E4dAb58d8ED4724f1Ef95B20Db0; - ///@dev Mainnet address of the `Dai` (multi-collateral) contract - address constant private DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F; - /// @dev Mainnet address of the `Chai` contract - address constant private CHAI_ADDRESS = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215; - /// @dev Mainnet address of the 0x DevUtils contract. - address constant private DEV_UTILS_ADDRESS = 0x74134CF88b21383713E096a5ecF59e297dc7f547; - /// @dev Kyber ETH pseudo-address. - address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - /// @dev Mainnet address of the dYdX contract. - address constant private DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e; - /// @dev Mainnet address of the GST2 contract - address constant private GST_ADDRESS = 0x0000000000b3F879cb30FE243b4Dfee438691c04; - /// @dev Mainnet address of the GST Collector - address constant private GST_COLLECTOR_ADDRESS = 0x000000D3b08566BE75A6DB803C03C85C0c1c5B96; - /// @dev Mainnet address of the mStable mUSD contract. - address constant private MUSD_ADDRESS = 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5; - /// @dev Mainnet address of the Mooniswap Registry contract - address constant private MOONISWAP_REGISTRY = 0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303; - /// @dev Mainnet address of the Shell contract - address constant private SHELL_CONTRACT = 0x2E703D658f8dd21709a7B458967aB4081F8D3d05; - /// @dev Mainnet address of the DODO Registry (ZOO) contract - address constant private DODO_REGISTRY = 0x3A97247DF274a17C59A3bd12735ea3FcDFb49950; - /// @dev Mainnet address of the DODO Helper contract - address constant private DODO_HELPER = 0x533dA777aeDCE766CEAe696bf90f8541A4bA80Eb; - /// @dev Mainnet address of the Bancor Registry contract - address constant private BANCOR_REGISTRY = 0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4; - - // // Ropsten addresses /////////////////////////////////////////////////////// - // /// @dev Mainnet address of the WETH contract. - // address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab; - // /// @dev Mainnet address of the KyberNetworkProxy contract. - // address constant private KYBER_NETWORK_PROXY_ADDRESS = 0xd719c34261e099Fdb33030ac8909d5788D3039C4; - // /// @dev Mainnet address of the `UniswapExchangeFactory` contract. - // address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351; - // /// @dev Mainnet address of the `UniswapV2Router01` contract. - // address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a; - // /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract. - // address constant private ETH2DAI_ADDRESS = address(0); - // /// @dev Mainnet address of the `ERC20BridgeProxy` contract - // address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xb344afeD348de15eb4a9e180205A2B0739628339; - // ///@dev Mainnet address of the `Dai` (multi-collateral) contract - // address constant private DAI_ADDRESS = address(0); - // /// @dev Mainnet address of the `Chai` contract - // address constant private CHAI_ADDRESS = address(0); - // /// @dev Mainnet address of the 0x DevUtils contract. - // address constant private DEV_UTILS_ADDRESS = 0xC812AF3f3fBC62F76ea4262576EC0f49dB8B7f1c; - // /// @dev Kyber ETH pseudo-address. - // address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - // /// @dev Mainnet address of the dYdX contract. - // address constant private DYDX_ADDRESS = address(0); - // /// @dev Mainnet address of the GST2 contract - // address constant private GST_ADDRESS = address(0); - // /// @dev Mainnet address of the GST Collector - // address constant private GST_COLLECTOR_ADDRESS = address(0); - // /// @dev Mainnet address of the mStable mUSD contract. - // address constant private MUSD_ADDRESS = 0x4E1000616990D83e56f4b5fC6CC8602DcfD20459; - - // // Rinkeby addresses /////////////////////////////////////////////////////// - // /// @dev Mainnet address of the WETH contract. - // address constant private WETH_ADDRESS = 0xc778417E063141139Fce010982780140Aa0cD5Ab; - // /// @dev Mainnet address of the KyberNetworkProxy contract. - // address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x0d5371e5EE23dec7DF251A8957279629aa79E9C5; - // /// @dev Mainnet address of the `UniswapExchangeFactory` contract. - // address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36; - // /// @dev Mainnet address of the `UniswapV2Router01` contract. - // address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a; - // /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract. - // address constant private ETH2DAI_ADDRESS = address(0); - // /// @dev Mainnet address of the `ERC20BridgeProxy` contract - // address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xA2AA4bEFED748Fba27a3bE7Dfd2C4b2c6DB1F49B; - // ///@dev Mainnet address of the `Dai` (multi-collateral) contract - // address constant private DAI_ADDRESS = address(0); - // /// @dev Mainnet address of the `Chai` contract - // address constant private CHAI_ADDRESS = address(0); - // /// @dev Mainnet address of the 0x DevUtils contract. - // address constant private DEV_UTILS_ADDRESS = 0x46B5BC959e8A754c0256FFF73bF34A52Ad5CdfA9; - // /// @dev Kyber ETH pseudo-address. - // address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - // /// @dev Mainnet address of the dYdX contract. - // address constant private DYDX_ADDRESS = address(0); - // /// @dev Mainnet address of the GST2 contract - // address constant private GST_ADDRESS = address(0); - // /// @dev Mainnet address of the GST Collector - // address constant private GST_COLLECTOR_ADDRESS = address(0); - // /// @dev Mainnet address of the mStable mUSD contract. - // address constant private MUSD_ADDRESS = address(0); - - // // Kovan addresses ///////////////////////////////////////////////////////// - // /// @dev Kovan address of the WETH contract. - // address constant private WETH_ADDRESS = 0xd0A1E359811322d97991E03f863a0C30C2cF029C; - // /// @dev Kovan address of the KyberNetworkProxy contract. - // address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D; - // /// @dev Kovan address of the `UniswapExchangeFactory` contract. - // address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30; - // /// @dev Kovan address of the `UniswapV2Router01` contract. - // address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a; - // /// @dev Kovan address of the Eth2Dai `MatchingMarket` contract. - // address constant private ETH2DAI_ADDRESS = 0xe325acB9765b02b8b418199bf9650972299235F4; - // /// @dev Kovan address of the `ERC20BridgeProxy` contract - // address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x3577552C1Fb7A44aD76BeEB7aB53251668A21F8D; - // /// @dev Kovan address of the `Chai` contract - // address constant private CHAI_ADDRESS = address(0); - // /// @dev Kovan address of the `Dai` (multi-collateral) contract - // address constant private DAI_ADDRESS = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa; - // /// @dev Kovan address of the 0x DevUtils contract. - // address constant private DEV_UTILS_ADDRESS = 0x9402639A828BdF4E9e4103ac3B69E1a6E522eB59; - // /// @dev Kyber ETH pseudo-address. - // address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - // /// @dev Kovan address of the dYdX contract. - // address constant private DYDX_ADDRESS = address(0); - // /// @dev Kovan address of the GST2 contract - // address constant private GST_ADDRESS = address(0); - // /// @dev Kovan address of the GST Collector - // address constant private GST_COLLECTOR_ADDRESS = address(0); - // /// @dev Mainnet address of the mStable mUSD contract. - // address constant private MUSD_ADDRESS = address(0); - - /// @dev Overridable way to get the `KyberNetworkProxy` address. - /// @return kyberAddress The `IKyberNetworkProxy` address. - function _getKyberNetworkProxyAddress() - virtual - internal - view - returns (address kyberAddress) - { - return KYBER_NETWORK_PROXY_ADDRESS; - } - - /// @dev Overridable way to get the `KyberHintHandler` address. - /// @return hintHandlerAddress The `IKyberHintHandler` address. - function _getKyberHintHandlerAddress() - virtual - internal - view - returns (address hintHandlerAddress) - { - return KYBER_HINT_HANDLER_ADDRESS; - } - - /// @dev Overridable way to get the WETH address. - /// @return wethAddress The WETH address. - function _getWethAddress() - internal - view - returns (address wethAddress) - { - return WETH_ADDRESS; - } - - /// @dev Overridable way to get the `UniswapExchangeFactory` address. - /// @return uniswapAddress The `UniswapExchangeFactory` address. - function _getUniswapExchangeFactoryAddress() - virtual - internal - view - returns (address uniswapAddress) - { - return UNISWAP_EXCHANGE_FACTORY_ADDRESS; - } - - /// @dev Overridable way to get the `UniswapV2Router01` address. - /// @return uniswapRouterAddress The `UniswapV2Router01` address. - function _getUniswapV2Router01Address() - virtual - internal - view - returns (address uniswapRouterAddress) - { - return UNISWAP_V2_ROUTER_01_ADDRESS; - } - - /// @dev An overridable way to retrieve the Eth2Dai `MatchingMarket` contract. - /// @return eth2daiAddress The Eth2Dai `MatchingMarket` contract. - function _getEth2DaiAddress() - virtual - internal - view - returns (address eth2daiAddress) - { - return ETH2DAI_ADDRESS; - } - - /// @dev An overridable way to retrieve the `ERC20BridgeProxy` contract. - /// @return erc20BridgeProxyAddress The `ERC20BridgeProxy` contract. - function _getERC20BridgeProxyAddress() - internal - view - returns (address erc20BridgeProxyAddress) - { - return ERC20_BRIDGE_PROXY_ADDRESS; - } - - /// @dev An overridable way to retrieve the `Dai` contract. - /// @return daiAddress The `Dai` contract. - function _getDaiAddress() - internal - view - returns (address daiAddress) - { - return DAI_ADDRESS; - } - - /// @dev An overridable way to retrieve the `Chai` contract. - /// @return chaiAddress The `Chai` contract. - function _getChaiAddress() - internal - view - returns (address chaiAddress) - { - return CHAI_ADDRESS; - } - - /// @dev An overridable way to retrieve the 0x `DevUtils` contract address. - /// @return devUtils The 0x `DevUtils` contract address. - function _getDevUtilsAddress() - internal - view - returns (address devUtils) - { - return DEV_UTILS_ADDRESS; - } - - /// @dev Overridable way to get the DyDx contract. - /// @return dydxAddress exchange The DyDx exchange contract. - function _getDydxAddress() - internal - view - returns (address dydxAddress) - { - return DYDX_ADDRESS; - } - - /// @dev An overridable way to retrieve the GST2 contract address. - /// @return gst The GST contract. - function _getGstAddress() - internal - view - returns (address gst) - { - return GST_ADDRESS; - } - - /// @dev An overridable way to retrieve the GST Collector address. - /// @return collector The GST collector address. - function _getGstCollectorAddress() - internal - view - returns (address collector) - { - return GST_COLLECTOR_ADDRESS; - } - - /// @dev An overridable way to retrieve the mStable mUSD address. - /// @return musd The mStable mUSD address. - function _getMUsdAddress() - internal - view - returns (address musd) - { - return MUSD_ADDRESS; - } - - /// @dev An overridable way to retrieve the Mooniswap registry address. - /// @return registry The Mooniswap registry address. - function _getMooniswapAddress() - internal - view - returns (address) - { - return MOONISWAP_REGISTRY; - } - - /// @dev An overridable way to retrieve the Shell contract address. - /// @return registry The Shell contract address. - function _getShellAddress() - internal - view - returns (address registry) - { - return SHELL_CONTRACT; - } - - /// @dev An overridable way to retrieve the DODO Registry contract address. - /// @return registry The DODO Registry contract address. - function _getDODORegistryAddress() - internal - view - returns (address registry) - { - return DODO_REGISTRY; - } - - /// @dev An overridable way to retrieve the DODO Helper contract address. - /// @return registry The DODO Helper contract address. - function _getDODOHelperAddress() - internal - view - returns (address registry) - { - return DODO_HELPER; - } - - /// @dev An overridable way to retrieve the Bancor Registry contract address. - /// @return registry The Bancor registry contract address. - function _getBancorRegistryAddress() - internal - view - returns (address registry) - { - return BANCOR_REGISTRY; - } -} diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index 7424919a50..d438d80317 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -33,7 +33,6 @@ import "./MStableSampler.sol"; import "./MooniswapSampler.sol"; import "./NativeOrderSampler.sol"; import "./ShellSampler.sol"; -import "./SushiSwapSampler.sol"; import "./TwoHopSampler.sol"; import "./UniswapSampler.sol"; import "./UniswapV2Sampler.sol"; @@ -54,7 +53,6 @@ contract ERC20BridgeSampler is MultiBridgeSampler, NativeOrderSampler, ShellSampler, - SushiSwapSampler, TwoHopSampler, UniswapSampler, UniswapV2Sampler, diff --git a/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol b/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol index 0f02f746d9..07d532ab88 100644 --- a/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol +++ b/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol @@ -20,25 +20,25 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IEth2Dai.sol"; import "./SamplerUtils.sol"; contract Eth2DaiSampler is - DeploymentConstants, SamplerUtils { /// @dev Base gas limit for Eth2Dai calls. uint256 constant private ETH2DAI_CALL_GAS = 1000e3; // 1m /// @dev Sample sell quotes from Eth2Dai/Oasis. + /// @param router Address of the Eth2Dai/Oasis contract /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromEth2Dai( + address router, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -52,12 +52,16 @@ contract Eth2DaiSampler is makerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { try - IEth2Dai(_getEth2DaiAddress()).getBuyAmount + IEth2Dai(router).getBuyAmount {gas: ETH2DAI_CALL_GAS} (makerToken, takerToken, takerTokenAmounts[i]) returns (uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; @@ -66,12 +70,14 @@ contract Eth2DaiSampler is } /// @dev Sample buy quotes from Eth2Dai/Oasis. + /// @param router Address of the Eth2Dai/Oasis contract /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Maker token sell amount for each sample. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromEth2Dai( + address router, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -85,12 +91,16 @@ contract Eth2DaiSampler is takerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { try - IEth2Dai(_getEth2DaiAddress()).getPayAmount + IEth2Dai(router).getPayAmount {gas: ETH2DAI_CALL_GAS} (takerToken, makerToken, makerTokenAmounts[i]) returns (uint256 amount) { takerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (takerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; diff --git a/packages/asset-swapper/contracts/src/KyberSampler.sol b/packages/asset-swapper/contracts/src/KyberSampler.sol index a605fff9be..17c91cc8f7 100644 --- a/packages/asset-swapper/contracts/src/KyberSampler.sol +++ b/packages/asset-swapper/contracts/src/KyberSampler.sol @@ -20,22 +20,30 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IKyberNetwork.sol"; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; contract KyberSampler is - DeploymentConstants, SamplerUtils, ApproximateBuys { /// @dev Gas limit for Kyber calls. uint256 constant private KYBER_CALL_GAS = 500e3; // 500k + /// @dev Kyber ETH pseudo-address. + address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + struct KyberSamplerOpts { + uint256 reserveOffset; + address hintHandler; + address networkProxy; + address weth; + bytes hint; + } /// @dev Sample sell quotes from Kyber. - /// @param reserveOffset The nth reserve + /// @param opts KyberSamplerOpts The nth reserve /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. @@ -43,7 +51,7 @@ contract KyberSampler is /// @return hint The hint for the selected reserve /// @return makerTokenAmounts Maker amounts bought at each taker token amount. function sampleSellsFromKyberNetwork( - uint256 reserveOffset, + KyberSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -53,31 +61,32 @@ contract KyberSampler is returns (bytes32 reserveId, bytes memory hint, uint256[] memory makerTokenAmounts) { _assertValidPair(makerToken, takerToken); - reserveId = _getNextReserveId(takerToken, makerToken, reserveOffset); + reserveId = _getNextReserveId(opts, takerToken, makerToken); if (reserveId == 0x0) { return (reserveId, hint, makerTokenAmounts); } - hint = this.encodeKyberHint(reserveId, takerToken, makerToken); + opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken); + hint = opts.hint; uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { uint256 value = this.sampleSellFromKyberNetwork( - hint, + opts, takerToken, makerToken, takerTokenAmounts[i] ); - // Return early if the source has no liquidity - if (value == 0) { - return (reserveId, hint, makerTokenAmounts); - } makerTokenAmounts[i] = value; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } } /// @dev Sample buy quotes from Kyber. - /// @param reserveOffset The nth reserve + /// @param opts KyberSamplerOpts The nth reserve /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. @@ -85,7 +94,7 @@ contract KyberSampler is /// @return hint The hint for the selected reserve /// @return takerTokenAmounts Taker amounts sold at each maker token amount. function sampleBuysFromKyberNetwork( - uint256 reserveOffset, + KyberSamplerOpts memory opts, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -96,16 +105,17 @@ contract KyberSampler is { _assertValidPair(makerToken, takerToken); - reserveId = _getNextReserveId(takerToken, makerToken, reserveOffset); + reserveId = _getNextReserveId(opts, takerToken, makerToken); if (reserveId == 0x0) { return (reserveId, hint, takerTokenAmounts); } - hint = this.encodeKyberHint(reserveId, takerToken, makerToken); + opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken); + hint = opts.hint; takerTokenAmounts = _sampleApproximateBuys( ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken, hint), - takerTokenData: abi.encode(takerToken, hint), + makerTokenData: abi.encode(makerToken, opts), + takerTokenData: abi.encode(takerToken, opts), getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber }), makerTokenAmounts @@ -114,6 +124,7 @@ contract KyberSampler is } function encodeKyberHint( + KyberSamplerOpts memory opts, bytes32 reserveId, address takerToken, address makerToken @@ -123,14 +134,14 @@ contract KyberSampler is returns (bytes memory hint) { // Build a hint selecting the single reserve - IKyberHintHandler kyberHint = IKyberHintHandler(_getKyberHintHandlerAddress()); + IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler); // All other reserves should be ignored with this hint bytes32[] memory selectedReserves = new bytes32[](1); selectedReserves[0] = reserveId; uint256[] memory emptySplits = new uint256[](0); - if (takerToken == _getWethAddress()) { + if (takerToken == opts.weth) { // ETH to Token try kyberHint.buildEthToTokenHint @@ -147,7 +158,7 @@ contract KyberSampler is } catch (bytes memory) { // Swallow failures, leaving all results as zero. } - } else if (makerToken == _getWethAddress()) { + } else if (makerToken == opts.weth) { // Token to ETH try kyberHint.buildTokenToEthHint @@ -199,13 +210,13 @@ contract KyberSampler is view returns (uint256) { - (address makerToken, bytes memory hint) = - abi.decode(makerTokenData, (address, bytes)); + (address makerToken, KyberSamplerOpts memory opts) = + abi.decode(makerTokenData, (address, KyberSamplerOpts)); (address takerToken, ) = - abi.decode(takerTokenData, (address, bytes)); + abi.decode(takerTokenData, (address, KyberSamplerOpts)); try this.sampleSellFromKyberNetwork - (hint, takerToken, makerToken, sellAmount) + (opts, takerToken, makerToken, sellAmount) returns (uint256 amount) { return amount; @@ -216,7 +227,7 @@ contract KyberSampler is } function sampleSellFromKyberNetwork( - bytes memory hint, + KyberSamplerOpts memory opts, address takerToken, address makerToken, uint256 takerTokenAmount @@ -226,19 +237,19 @@ contract KyberSampler is returns (uint256 makerTokenAmount) { // If there is no hint do not continue - if (hint.length == 0) { + if (opts.hint.length == 0) { return 0; } try - IKyberNetworkProxy(_getKyberNetworkProxyAddress()).getExpectedRateAfterFee + IKyberNetworkProxy(opts.networkProxy).getExpectedRateAfterFee {gas: KYBER_CALL_GAS} ( - takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken, - makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken, + takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken, + makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken, takerTokenAmount, 0, // fee - hint + opts.hint ) returns (uint256 rate) { @@ -258,28 +269,28 @@ contract KyberSampler is } function _getNextReserveId( + KyberSamplerOpts memory opts, address takerToken, - address makerToken, - uint256 reserveOffset + address makerToken ) internal view returns (bytes32 reserveId) { // Fetch the registered reserves for this pair - IKyberHintHandler kyberHint = IKyberHintHandler(_getKyberHintHandlerAddress()); + IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler); (bytes32[] memory reserveIds, ,) = kyberHint.getTradingReserves( - takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken, - makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken, + takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken, + makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken, true, new bytes(0) // empty hint ); - if (reserveOffset >= reserveIds.length) { + if (opts.reserveOffset >= reserveIds.length) { return 0x0; } - reserveId = reserveIds[reserveOffset]; + reserveId = reserveIds[opts.reserveOffset]; // Ignore Kyber Bridged Reserves (0xbb) if (uint256(reserveId >> 248) == 0xbb) { return 0x0; diff --git a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol b/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol index 91debd823e..a5c871c6e9 100644 --- a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol +++ b/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol @@ -66,6 +66,10 @@ contract LiquidityProviderSampler is returns (uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; diff --git a/packages/asset-swapper/contracts/src/MStableSampler.sol b/packages/asset-swapper/contracts/src/MStableSampler.sol index e9cef0008f..32be511fb8 100644 --- a/packages/asset-swapper/contracts/src/MStableSampler.sol +++ b/packages/asset-swapper/contracts/src/MStableSampler.sol @@ -20,27 +20,27 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IMStable.sol"; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; contract MStableSampler is - DeploymentConstants, SamplerUtils, ApproximateBuys { /// @dev Default gas limit for mStable calls. uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k - /// @dev Sample sell quotes from the mStable mUSD contract + /// @dev Sample sell quotes from the mStable contract + /// @param router Address of the mStable contract /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromMStable( + address router, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -55,12 +55,16 @@ contract MStableSampler is for (uint256 i = 0; i < numSamples; i++) { try - IMStable(_getMUsdAddress()).getSwapOutput + IMStable(router).getSwapOutput {gas: DEFAULT_CALL_GAS} (takerToken, makerToken, takerTokenAmounts[i]) returns (bool, string memory, uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; @@ -68,13 +72,15 @@ contract MStableSampler is } } - /// @dev Sample buy quotes from MStable mUSD contract + /// @dev Sample buy quotes from MStable contract + /// @param router Address of the mStable contract /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromMStable( + address router, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -85,8 +91,8 @@ contract MStableSampler is { return _sampleApproximateBuys( ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(makerToken), - takerTokenData: abi.encode(takerToken), + makerTokenData: abi.encode(makerToken, router), + takerTokenData: abi.encode(takerToken, router), getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable }), makerTokenAmounts @@ -102,13 +108,13 @@ contract MStableSampler is view returns (uint256 buyAmount) { - (address takerToken) = - abi.decode(takerTokenData, (address)); + (address takerToken, address router) = + abi.decode(takerTokenData, (address, address)); (address makerToken) = abi.decode(makerTokenData, (address)); try this.sampleSellsFromMStable - (takerToken, makerToken, _toSingleValueArray(sellAmount)) + (router, takerToken, makerToken, _toSingleValueArray(sellAmount)) returns (uint256[] memory amounts) { return amounts[0]; diff --git a/packages/asset-swapper/contracts/src/MooniswapSampler.sol b/packages/asset-swapper/contracts/src/MooniswapSampler.sol index 2603f3db5a..32ee080450 100644 --- a/packages/asset-swapper/contracts/src/MooniswapSampler.sol +++ b/packages/asset-swapper/contracts/src/MooniswapSampler.sol @@ -20,14 +20,12 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IMooniswap.sol"; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; contract MooniswapSampler is - DeploymentConstants, SamplerUtils, ApproximateBuys { @@ -56,25 +54,22 @@ contract MooniswapSampler is uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); - address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken; - address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken; - for (uint256 i = 0; i < numSamples; i++) { uint256 buyAmount = sampleSingleSellFromMooniswapPool( registry, - mooniswapTakerToken, - mooniswapMakerToken, + takerToken, + makerToken, takerTokenAmounts[i] ); - // Exit early if the amount is too high for the source to serve - if (buyAmount == 0) { + makerTokenAmounts[i] = buyAmount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { break; } - makerTokenAmounts[i] = buyAmount; } pool = IMooniswap( - IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken) + IMooniswapRegistry(registry).pools(takerToken, makerToken) ); } @@ -139,20 +134,17 @@ contract MooniswapSampler is uint256 numSamples = makerTokenAmounts.length; takerTokenAmounts = new uint256[](numSamples); - address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken; - address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken; - takerTokenAmounts = _sampleApproximateBuys( ApproximateBuyQuoteOpts({ - makerTokenData: abi.encode(registry, mooniswapMakerToken), - takerTokenData: abi.encode(registry, mooniswapTakerToken), + makerTokenData: abi.encode(registry, makerToken), + takerTokenData: abi.encode(registry, takerToken), getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap }), makerTokenAmounts ); pool = IMooniswap( - IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken) + IMooniswapRegistry(registry).pools(takerToken, makerToken) ); } diff --git a/packages/asset-swapper/contracts/src/ShellSampler.sol b/packages/asset-swapper/contracts/src/ShellSampler.sol index 51e25591cf..8435f6674a 100644 --- a/packages/asset-swapper/contracts/src/ShellSampler.sol +++ b/packages/asset-swapper/contracts/src/ShellSampler.sol @@ -20,11 +20,9 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IShell.sol"; -contract ShellSampler is - DeploymentConstants +contract ShellSampler { /// @dev Default gas limit for Shell calls. uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k @@ -58,6 +56,10 @@ contract ShellSampler is returns (uint256 amount) { makerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; @@ -94,6 +96,10 @@ contract ShellSampler is returns (uint256 amount) { takerTokenAmounts[i] = amount; + // Break early if there are 0 amounts + if (takerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; diff --git a/packages/asset-swapper/contracts/src/SushiSwapSampler.sol b/packages/asset-swapper/contracts/src/SushiSwapSampler.sol deleted file mode 100644 index 753bcd4119..0000000000 --- a/packages/asset-swapper/contracts/src/SushiSwapSampler.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -/* - - 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.6; -pragma experimental ABIEncoderV2; - -import "./DeploymentConstants.sol"; -import "./interfaces/IUniswapV2Router01.sol"; - - -contract SushiSwapSampler is - DeploymentConstants -{ - /// @dev Gas limit for SushiSwap calls. - uint256 constant private SUSHISWAP_CALL_GAS = 150e3; // 150k - - /// @dev Sample sell quotes from SushiSwap. - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromSushiSwap( - address router, - address[] memory path, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - uint256 numSamples = takerTokenAmounts.length; - makerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IUniswapV2Router01(router).getAmountsOut - {gas: SUSHISWAP_CALL_GAS} - (takerTokenAmounts[i], path) - returns (uint256[] memory amounts) - { - makerTokenAmounts[i] = amounts[path.length - 1]; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } - - /// @dev Sample buy quotes from SushiSwap - /// @param router Router to look up tokens and amounts - /// @param path Token route. Should be takerToken -> makerToken. - /// @param makerTokenAmounts Maker token buy amount for each sample. - /// @return takerTokenAmounts Taker amounts sold at each maker token - /// amount. - function sampleBuysFromSushiSwap( - address router, - address[] memory path, - uint256[] memory makerTokenAmounts - ) - public - view - returns (uint256[] memory takerTokenAmounts) - { - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - for (uint256 i = 0; i < numSamples; i++) { - try - IUniswapV2Router01(router).getAmountsIn - {gas: SUSHISWAP_CALL_GAS} - (makerTokenAmounts[i], path) - returns (uint256[] memory amounts) - { - takerTokenAmounts[i] = amounts[0]; - } catch (bytes memory) { - // Swallow failures, leaving all results as zero. - break; - } - } - } -} diff --git a/packages/asset-swapper/contracts/src/UniswapSampler.sol b/packages/asset-swapper/contracts/src/UniswapSampler.sol index 1aafbd95ee..329461cef9 100644 --- a/packages/asset-swapper/contracts/src/UniswapSampler.sol +++ b/packages/asset-swapper/contracts/src/UniswapSampler.sol @@ -20,7 +20,6 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IUniswapExchangeQuotes.sol"; import "./SamplerUtils.sol"; @@ -37,21 +36,20 @@ interface IUniswapExchangeFactory { contract UniswapSampler is - DeploymentConstants, SamplerUtils { /// @dev Gas limit for Uniswap calls. uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k - // @dev The BNB token is poisoned on uniswap v1. - address constant private BAD_MAKER_TOKEN = 0xB8c77482e45F1F44dE1745F52C74426C631bDD52; /// @dev Sample sell quotes from Uniswap. + /// @param router Address of the Uniswap Router /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromUniswap( + address router, address takerToken, address makerToken, uint256[] memory takerTokenAmounts @@ -63,23 +61,20 @@ contract UniswapSampler is _assertValidPair(makerToken, takerToken); uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); - if (makerToken == BAD_MAKER_TOKEN) { - // BNB is poisoned on v1. You can only sell to it. - return makerTokenAmounts; - } - IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken); + + IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ? + IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken); + IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ? + IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken); for (uint256 i = 0; i < numSamples; i++) { bool didSucceed = true; - if (makerToken == _getWethAddress()) { + if (makerToken == address(0)) { (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( address(takerTokenExchange), takerTokenExchange.getTokenToEthInputPrice.selector, takerTokenAmounts[i] ); - } else if (takerToken == _getWethAddress()) { + } else if (takerToken == address(0)) { (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( address(makerTokenExchange), makerTokenExchange.getEthToTokenInputPrice.selector, @@ -102,7 +97,8 @@ contract UniswapSampler is makerTokenAmounts[i] = 0; } } - if (!didSucceed) { + // Break early if amounts are 0 + if (!didSucceed || makerTokenAmounts[i] == 0) { break; } } @@ -115,6 +111,7 @@ contract UniswapSampler is /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromUniswap( + address router, address takerToken, address makerToken, uint256[] memory makerTokenAmounts @@ -126,23 +123,20 @@ contract UniswapSampler is _assertValidPair(makerToken, takerToken); uint256 numSamples = makerTokenAmounts.length; takerTokenAmounts = new uint256[](numSamples); - if (makerToken == BAD_MAKER_TOKEN) { - // BNB is poisoned on v1. You can only sell to it. - return takerTokenAmounts; - } - IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ? - IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken); + + IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ? + IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken); + IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ? + IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken); for (uint256 i = 0; i < numSamples; i++) { bool didSucceed = true; - if (makerToken == _getWethAddress()) { + if (makerToken == address(0)) { (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( address(takerTokenExchange), takerTokenExchange.getTokenToEthOutputPrice.selector, makerTokenAmounts[i] ); - } else if (takerToken == _getWethAddress()) { + } else if (takerToken == address(0)) { (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction( address(makerTokenExchange), makerTokenExchange.getEthToTokenOutputPrice.selector, @@ -165,7 +159,8 @@ contract UniswapSampler is takerTokenAmounts[i] = 0; } } - if (!didSucceed) { + // Break early if amounts are 0 + if (!didSucceed || takerTokenAmounts[i] == 0) { break; } } @@ -203,15 +198,16 @@ contract UniswapSampler is /// @dev Retrive an existing Uniswap exchange contract. /// Throws if the exchange does not exist. + /// @param router Address of the Uniswap router. /// @param tokenAddress Address of the token contract. /// @return exchange `IUniswapExchangeQuotes` for the token. - function _getUniswapExchange(address tokenAddress) + function _getUniswapExchange(address router, address tokenAddress) private view returns (IUniswapExchangeQuotes exchange) { exchange = IUniswapExchangeQuotes( - address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress()) + address(IUniswapExchangeFactory(router) .getExchange(tokenAddress)) ); } diff --git a/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol b/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol index 09c9365e51..de2f2ee29b 100644 --- a/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol +++ b/packages/asset-swapper/contracts/src/UniswapV2Sampler.sol @@ -20,12 +20,10 @@ pragma solidity ^0.6; pragma experimental ABIEncoderV2; -import "./DeploymentConstants.sol"; import "./interfaces/IUniswapV2Router01.sol"; -contract UniswapV2Sampler is - DeploymentConstants +contract UniswapV2Sampler { /// @dev Gas limit for UniswapV2 calls. uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k @@ -55,6 +53,10 @@ contract UniswapV2Sampler is returns (uint256[] memory amounts) { makerTokenAmounts[i] = amounts[path.length - 1]; + // Break early if there are 0 amounts + if (makerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; @@ -87,6 +89,10 @@ contract UniswapV2Sampler is returns (uint256[] memory amounts) { takerTokenAmounts[i] = amounts[0]; + // Break early if there are 0 amounts + if (takerTokenAmounts[i] == 0) { + break; + } } catch (bytes memory) { // Swallow failures, leaving all results as zero. break; diff --git a/packages/asset-swapper/contracts/test/TestERC20BridgeSampler.sol b/packages/asset-swapper/contracts/test/TestERC20BridgeSampler.sol index b7ccd1c702..9c31ef99a5 100644 --- a/packages/asset-swapper/contracts/test/TestERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/test/TestERC20BridgeSampler.sol @@ -88,6 +88,25 @@ library LibDeterministicQuotes { } } +contract TestDeploymentConstants { + + // solhint-disable separate-by-one-line-in-contract + + // Mainnet addresses /////////////////////////////////////////////////////// + /// @dev Mainnet address of the WETH contract. + address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + + /// @dev Overridable way to get the WETH address. + /// @return wethAddress The WETH address. + function _getWethAddress() + internal + view + returns (address wethAddress) + { + return WETH_ADDRESS; + } + +} contract FailTrigger { @@ -109,7 +128,7 @@ contract FailTrigger { contract TestERC20BridgeSamplerUniswapExchange is IUniswapExchangeQuotes, - DeploymentConstants, + TestDeploymentConstants, FailTrigger { bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab; @@ -198,7 +217,7 @@ contract TestERC20BridgeSamplerUniswapExchange is contract TestERC20BridgeSamplerUniswapV2Router01 is IUniswapV2Router01, - DeploymentConstants, + TestDeploymentConstants, FailTrigger { bytes32 constant private SALT = 0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1; @@ -249,7 +268,7 @@ contract TestERC20BridgeSamplerUniswapV2Router01 is // solhint-disable space-after-comma contract TestERC20BridgeSamplerKyberNetwork is - DeploymentConstants, + TestDeploymentConstants, FailTrigger { bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7; @@ -357,24 +376,6 @@ contract TestERC20BridgeSamplerKyberNetwork is toToken ); } - - function _getKyberNetworkProxyAddress() - override - internal - view - returns (address) - { - return address(this); - } - - function _getKyberHintHandlerAddress() - override - internal - view - returns (address) - { - return address(this); - } } @@ -502,54 +503,4 @@ contract TestERC20BridgeSampler is { return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress); } - - // Overriden to point to a custom contract. - function _getEth2DaiAddress() - override - internal - view - returns (address eth2daiAddress) - { - return address(eth2Dai); - } - - // Overriden to point to a custom contract. - function _getUniswapExchangeFactoryAddress() - override - internal - view - returns (address uniswapAddress) - { - return address(uniswap); - } - - // Overriden to point to a custom contract. - function _getUniswapV2Router01Address() - override - internal - view - returns (address uniswapV2RouterAddress) - { - return address(uniswapV2Router); - } - - // Overriden to point to a custom contract. - function _getKyberNetworkProxyAddress() - override - internal - view - returns (address kyberAddress) - { - return address(kyber); - } - - // Overriden to point to a custom contract. - function _getKyberHintHandlerAddress() - override - internal - view - returns (address kyberAddress) - { - return address(kyber); - } } diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 4956227135..ffa3abe899 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -38,7 +38,7 @@ "config": { "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json", "postpublish": { "assets": [] } diff --git a/packages/asset-swapper/src/constants.ts b/packages/asset-swapper/src/constants.ts index b1076e8d7f..1a370ead9d 100644 --- a/packages/asset-swapper/src/constants.ts +++ b/packages/asset-swapper/src/constants.ts @@ -15,8 +15,8 @@ import { } from './types'; import { DEFAULT_GET_MARKET_ORDERS_OPTS, - DEFAULT_INTERMEDIATE_TOKENS, - DEFAULT_TOKEN_ADJACENCY_GRAPH, + DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID, + DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID, } from './utils/market_operation_utils/constants'; const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json'; @@ -54,7 +54,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = { makerAssetOfferings: {}, txOriginBlacklist: new Set(), }, - tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH, + tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID[ChainId.Mainnet], }; const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = { @@ -112,7 +112,7 @@ export const constants = { ONE_SECOND_MS, ONE_MINUTE_MS, DEFAULT_SWAP_QUOTER_OPTS, - DEFAULT_INTERMEDIATE_TOKENS, + DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID, DEFAULT_SWAP_QUOTE_REQUEST_OPTS, DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS, DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS, diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 7a8ff58333..bc94d5f937 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -110,9 +110,11 @@ export { } from './types'; export { affiliateFeeUtils } from './utils/affiliate_fee_utils'; export { - DEFAULT_TOKEN_ADJACENCY_GRAPH, + DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID, DEFAULT_GAS_SCHEDULE, SOURCE_FLAGS, + BUY_SOURCE_FILTER_BY_CHAIN_ID, + SELL_SOURCE_FILTER_BY_CHAIN_ID, } from './utils/market_operation_utils/constants'; export { Parameters, @@ -146,12 +148,7 @@ export { NativeLimitOrderFillData, NativeFillData, OptimizedMarketOrder, - SnowSwapFillData, - SnowSwapInfo, SourceQuoteOperation, - SushiSwapFillData, - SwerveFillData, - SwerveInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './utils/market_operation_utils/types'; diff --git a/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts index 9a25b1197d..d223e1a505 100644 --- a/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts +++ b/packages/asset-swapper/src/quote_consumers/exchange_proxy_swap_quote_consumer.ts @@ -1,6 +1,6 @@ -import { ContractAddresses } from '@0x/contract-addresses'; -import { WETH9Contract } from '@0x/contracts-erc20'; -import { IZeroExContract, MultiplexFeatureContract } from '@0x/contracts-zero-ex'; +import { ChainId, ContractAddresses } from '@0x/contract-addresses'; +import { IZeroExContract, WETH9Contract } from '@0x/contract-wrappers'; +import { MultiplexFeatureContract } from '@0x/contracts-zero-ex'; import { encodeAffiliateFeeTransformerData, encodeCurveLiquidityProviderData, @@ -34,6 +34,7 @@ import { assert } from '../utils/assert'; import { CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID, MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID, + NATIVE_FEE_TOKEN_BY_CHAIN_ID, } from '../utils/market_operation_utils/constants'; import { poolEncoder } from '../utils/market_operation_utils/orders'; import { @@ -62,10 +63,16 @@ import { // tslint:disable-next-line:custom-no-magic-numbers const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants; +const PANCAKE_SWAP_FORKS = [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.BakerySwap, ERC20BridgeSource.SushiSwap]; +const DUMMY_WETH_CONTRACT = new WETH9Contract(NULL_ADDRESS, { + sendAsync(): void { + return; + }, +} as any); export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { public readonly provider: ZeroExProvider; - public readonly chainId: number; + public readonly chainId: ChainId; public readonly transformerNonces: { wethTransformer: number; payTakerTransformer: number; @@ -139,6 +146,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { // VIP routes. if ( + this.chainId === ChainId.Mainnet && isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap]) ) { const source = quote.orders[0].source; @@ -167,7 +175,44 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { }; } - if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])) { + if ( + this.chainId === ChainId.BSC && + isDirectSwapCompatible(quote, optsWithDefaults, [ + ERC20BridgeSource.PancakeSwap, + ERC20BridgeSource.BakerySwap, + ERC20BridgeSource.SushiSwap, + ]) + ) { + const source = quote.orders[0].source; + const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder).fillData; + return { + calldataHexString: this._exchangeProxy + .sellToPancakeSwap( + fillData.tokenAddressPath.map((a, i) => { + if (i === 0 && isFromETH) { + return ETH_TOKEN_ADDRESS; + } + if (i === fillData.tokenAddressPath.length - 1 && isToETH) { + return ETH_TOKEN_ADDRESS; + } + return a; + }), + sellAmount, + minBuyAmount, + PANCAKE_SWAP_FORKS.indexOf(source), + ) + .getABIEncodedTransactionData(), + ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT, + toAddress: this._exchangeProxy.address, + allowanceTarget: this._exchangeProxy.address, + gasOverhead: ZERO_AMOUNT, + }; + } + + if ( + this.chainId === ChainId.Mainnet && + isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider]) + ) { const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder).fillData; const target = fillData.poolAddress; return { @@ -189,7 +234,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { }; } - if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])) { + if ( + this.chainId === ChainId.Mainnet && + isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) + ) { const fillData = quote.orders[0].fills[0].fillData as CurveFillData; return { calldataHexString: this._exchangeProxy @@ -215,7 +263,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { }; } - if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])) { + if ( + this.chainId === ChainId.Mainnet && + isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap]) + ) { const fillData = quote.orders[0].fills[0].fillData as MooniswapFillData; return { calldataHexString: this._exchangeProxy @@ -236,7 +287,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { }; } - if (isMultiplexBatchFillCompatible(quote, optsWithDefaults)) { + if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) { return { calldataHexString: this._encodeMultiplexBatchFillCalldata(quote), ethAmount, @@ -245,7 +296,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { gasOverhead: ZERO_AMOUNT, }; } - if (isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) { + if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) { return { calldataHexString: this._encodeMultiplexMultiHopFillCalldata(quote, optsWithDefaults), ethAmount, @@ -317,7 +368,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { transforms.push({ deploymentNonce: this.transformerNonces.wethTransformer, data: encodeWethTransformerData({ - token: this.contractAddresses.etherToken, + token: NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId], amount: MAX_UINT256, }), }); @@ -491,11 +542,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { } private _encodeMultiplexMultiHopFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string { - const weth = new WETH9Contract(NULL_ADDRESS, this.provider); const wrappedMultiHopCalls = []; if (opts.isFromETH) { wrappedMultiHopCalls.push({ - selector: weth.getSelector('deposit'), + selector: DUMMY_WETH_CONTRACT.getSelector('deposit'), data: NULL_BYTES, }); } @@ -532,7 +582,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { } if (opts.isToETH) { wrappedMultiHopCalls.push({ - selector: weth.getSelector('withdraw'), + selector: DUMMY_WETH_CONTRACT.getSelector('withdraw'), data: NULL_BYTES, }); } diff --git a/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts b/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts index b298802e07..cf8165f6ae 100644 --- a/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts +++ b/packages/asset-swapper/src/quote_consumers/quote_consumer_utils.ts @@ -3,7 +3,7 @@ import { FillQuoteTransformerData, FillQuoteTransformerOrderType } from '@0x/pro import { AffiliateFeeType, ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types'; import { createBridgeDataForBridgeOrder, - getERC20BridgeSourceToBridgeSource, + getErc20BridgeSourceToBridgeSource, } from '../utils/market_operation_utils/orders'; import { ERC20BridgeSource, @@ -128,7 +128,7 @@ export function getFQTTransformerDataFromOptimizedOrders( bridgeData: createBridgeDataForBridgeOrder(order), makerTokenAmount: order.makerAmount, takerTokenAmount: order.takerAmount, - source: getERC20BridgeSourceToBridgeSource(order.source), + source: getErc20BridgeSourceToBridgeSource(order.source), }); } else if (isOptimizedLimitOrder(order)) { fqtData.limitOrders.push({ diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index c6d678996d..1717f37c56 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -132,6 +132,7 @@ export class SwapQuoter { this._marketOperationUtils = new MarketOperationUtils( new DexOrderSampler( + this.chainId, samplerContract, samplerOverrides, undefined, // balancer pool cache diff --git a/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts index 03e5b1e817..33b97a09a3 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/bridge_source_utils.ts @@ -1,15 +1,25 @@ +import { ChainId } from '@0x/contract-addresses'; import { BigNumber, NULL_BYTES } from '@0x/utils'; import { + BAKERYSWAP_ROUTER_BY_CHAIN_ID, + BELT_BSC_INFOS, + CRYPTO_COM_ROUTER_BY_CHAIN_ID, + ELLIPSIS_BSC_INFOS, KYBER_BRIDGED_LIQUIDITY_PREFIX, MAINNET_CURVE_INFOS, - MAINNET_SHELL_POOLS, MAINNET_SNOWSWAP_INFOS, MAINNET_SWERVE_INFOS, MAX_DODOV2_POOLS_QUERIED, MAX_KYBER_RESERVES_QUERIED, + NERVE_BSC_INFOS, + NULL_ADDRESS, + PANCAKESWAP_ROUTER_BY_CHAIN_ID, + SHELL_POOLS_BY_CHAIN_ID, + SUSHISWAP_ROUTER_BY_CHAIN_ID, + UNISWAPV2_ROUTER_BY_CHAIN_ID, } from './constants'; -import { CurveInfo, SnowSwapInfo, SwerveInfo } from './types'; +import { CurveInfo, ERC20BridgeSource } from './types'; /** * Filter Kyber reserves which should not be used (0xbb bridged reserves) @@ -19,6 +29,11 @@ export function isAllowedKyberReserveId(reserveId: string): boolean { return reserveId !== NULL_BYTES && !reserveId.startsWith(KYBER_BRIDGED_LIQUIDITY_PREFIX); } +// tslint:disable-next-line: completed-docs ban-types +export function isValidAddress(address: string | String): address is string { + return (typeof address === 'string' || address instanceof String) && address.toString() !== NULL_ADDRESS; +} + /** * Returns the offsets to be used to discover Kyber reserves */ @@ -36,14 +51,20 @@ export function getDodoV2Offsets(): BigNumber[] { } // tslint:disable completed-docs -export function getShellsForPair(takerToken: string, makerToken: string): string[] { - return Object.values(MAINNET_SHELL_POOLS) +export function getShellsForPair(chainId: ChainId, takerToken: string, makerToken: string): string[] { + if (chainId !== ChainId.Mainnet) { + return []; + } + return Object.values(SHELL_POOLS_BY_CHAIN_ID[chainId]) .filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))) .map(i => i.poolAddress); } // tslint:disable completed-docs -export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] { +export function getCurveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.Mainnet) { + return []; + } return Object.values(MAINNET_CURVE_INFOS).filter(c => [makerToken, takerToken].every( t => @@ -53,7 +74,10 @@ export function getCurveInfosForPair(takerToken: string, makerToken: string): Cu ); } -export function getSwerveInfosForPair(takerToken: string, makerToken: string): SwerveInfo[] { +export function getSwerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.Mainnet) { + return []; + } return Object.values(MAINNET_SWERVE_INFOS).filter(c => [makerToken, takerToken].every( t => @@ -63,7 +87,10 @@ export function getSwerveInfosForPair(takerToken: string, makerToken: string): S ); } -export function getSnowSwapInfosForPair(takerToken: string, makerToken: string): SnowSwapInfo[] { +export function getSnowSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.Mainnet) { + return []; + } return Object.values(MAINNET_SNOWSWAP_INFOS).filter(c => [makerToken, takerToken].every( t => @@ -72,3 +99,107 @@ export function getSnowSwapInfosForPair(takerToken: string, makerToken: string): ), ); } + +export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.BSC) { + return []; + } + return Object.values(NERVE_BSC_INFOS).filter(c => + [makerToken, takerToken].every( + t => + (c.tokens.includes(t) && c.metaToken === undefined) || + (c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)), + ), + ); +} + +export function getBeltInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.BSC) { + return []; + } + return Object.values(BELT_BSC_INFOS).filter(c => + [makerToken, takerToken].every( + t => + (c.tokens.includes(t) && c.metaToken === undefined) || + (c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)), + ), + ); +} + +export function getEllipsisInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] { + if (chainId !== ChainId.BSC) { + return []; + } + return Object.values(ELLIPSIS_BSC_INFOS).filter(c => + [makerToken, takerToken].every( + t => + (c.tokens.includes(t) && c.metaToken === undefined) || + (c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)), + ), + ); +} + +export function getCurveLikeInfosForPair( + chainId: ChainId, + takerToken: string, + makerToken: string, + source: + | ERC20BridgeSource.Curve + | ERC20BridgeSource.Swerve + | ERC20BridgeSource.SnowSwap + | ERC20BridgeSource.Nerve + | ERC20BridgeSource.Belt + | ERC20BridgeSource.Ellipsis, +): CurveInfo[] { + switch (source) { + case ERC20BridgeSource.Curve: + return getCurveInfosForPair(chainId, takerToken, makerToken); + case ERC20BridgeSource.Swerve: + return getSwerveInfosForPair(chainId, takerToken, makerToken); + case ERC20BridgeSource.SnowSwap: + return getSnowSwapInfosForPair(chainId, takerToken, makerToken); + case ERC20BridgeSource.Nerve: + return getNerveInfosForPair(chainId, takerToken, makerToken); + case ERC20BridgeSource.Belt: + return getBeltInfosForPair(chainId, takerToken, makerToken); + case ERC20BridgeSource.Ellipsis: + return getEllipsisInfosForPair(chainId, takerToken, makerToken); + default: + throw new Error(`Unknown Curve like source ${source}`); + } +} + +export function uniswapV2LikeRouterAddress( + chainId: ChainId, + source: + | ERC20BridgeSource.UniswapV2 + | ERC20BridgeSource.SushiSwap + | ERC20BridgeSource.CryptoCom + | ERC20BridgeSource.PancakeSwap + | ERC20BridgeSource.BakerySwap, +): string { + switch (source) { + case ERC20BridgeSource.UniswapV2: + return UNISWAPV2_ROUTER_BY_CHAIN_ID[chainId]; + case ERC20BridgeSource.SushiSwap: + return SUSHISWAP_ROUTER_BY_CHAIN_ID[chainId]; + case ERC20BridgeSource.CryptoCom: + return CRYPTO_COM_ROUTER_BY_CHAIN_ID[chainId]; + case ERC20BridgeSource.PancakeSwap: + return PANCAKESWAP_ROUTER_BY_CHAIN_ID[chainId]; + case ERC20BridgeSource.BakerySwap: + return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId]; + default: + throw new Error(`Unknown UniswapV2 like source ${source}`); + } +} + +const BAD_TOKENS_BY_SOURCE: Partial<{ [key in ERC20BridgeSource]: string[] }> = { + [ERC20BridgeSource.Uniswap]: [ + '0xb8c77482e45f1f44de1745f52c74426c631bdd52', // BNB + ], +}; + +export function isBadTokenForSource(token: string, source: ERC20BridgeSource): boolean { + return (BAD_TOKENS_BY_SOURCE[source] || []).includes(token.toLowerCase()); +} 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 f8d09491e3..7172e7727c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -1,3 +1,4 @@ +import { ChainId } from '@0x/contract-addresses'; import { FillQuoteTransformerOrderType } from '@0x/protocol-utils'; import { BigNumber } from '@0x/utils'; @@ -14,11 +15,10 @@ import { FeeSchedule, FillData, GetMarketOrdersOpts, + KyberSamplerOpts, LiquidityProviderFillData, LiquidityProviderRegistry, MultiHopFillData, - SnowSwapFillData, - SushiSwapFillData, TokenAdjacencyGraph, UniswapV2FillData, } from './types'; @@ -38,59 +38,115 @@ export const NULL_BYTES = '0x'; export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; export const COMPARISON_PRICE_DECIMALS = 10; +function valueByChainId(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } { + // TODO I don't like this but iterating through enums is weird + return { + [ChainId.Mainnet]: defaultValue, + [ChainId.Ropsten]: defaultValue, + [ChainId.Rinkeby]: defaultValue, + [ChainId.Kovan]: defaultValue, + [ChainId.Ganache]: defaultValue, + [ChainId.BSC]: defaultValue, + ...(rest || {}), + }; +} + /** * Valid sources for market sell. */ -export const SELL_SOURCE_FILTER = new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Kyber, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - ERC20BridgeSource.Bancor, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Mooniswap, - ERC20BridgeSource.Swerve, - ERC20BridgeSource.SnowSwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.Shell, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Cream, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.CryptoCom, - ERC20BridgeSource.Linkswap, -]); +export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: new SourceFilters([ + ERC20BridgeSource.Native, + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Eth2Dai, + ERC20BridgeSource.Kyber, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, + ERC20BridgeSource.Bancor, + ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.Swerve, + ERC20BridgeSource.SnowSwap, + ERC20BridgeSource.SushiSwap, + ERC20BridgeSource.Shell, + ERC20BridgeSource.MultiHop, + ERC20BridgeSource.Dodo, + ERC20BridgeSource.DodoV2, + ERC20BridgeSource.Cream, + ERC20BridgeSource.LiquidityProvider, + ERC20BridgeSource.CryptoCom, + ERC20BridgeSource.Linkswap, + ]), + [ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.BSC]: new SourceFilters([ + ERC20BridgeSource.BakerySwap, + ERC20BridgeSource.Belt, + ERC20BridgeSource.Dodo, + ERC20BridgeSource.DodoV2, + ERC20BridgeSource.Ellipsis, + ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.MultiHop, + ERC20BridgeSource.Nerve, + ERC20BridgeSource.PancakeSwap, + ERC20BridgeSource.SushiSwap, + ]), + }, + + new SourceFilters([]), +); /** * Valid sources for market buy. */ -export const BUY_SOURCE_FILTER = new SourceFilters([ - ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Kyber, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - // ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler - ERC20BridgeSource.MStable, - ERC20BridgeSource.Mooniswap, - ERC20BridgeSource.Shell, - ERC20BridgeSource.Swerve, - ERC20BridgeSource.SnowSwap, - ERC20BridgeSource.SushiSwap, - ERC20BridgeSource.MultiHop, - ERC20BridgeSource.Dodo, - ERC20BridgeSource.DodoV2, - ERC20BridgeSource.Cream, - ERC20BridgeSource.LiquidityProvider, - ERC20BridgeSource.CryptoCom, - ERC20BridgeSource.Linkswap, -]); +export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: new SourceFilters([ + ERC20BridgeSource.Native, + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Eth2Dai, + ERC20BridgeSource.Kyber, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, + // ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler + ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.Shell, + ERC20BridgeSource.Swerve, + ERC20BridgeSource.SnowSwap, + ERC20BridgeSource.SushiSwap, + ERC20BridgeSource.MultiHop, + ERC20BridgeSource.Dodo, + ERC20BridgeSource.DodoV2, + ERC20BridgeSource.Cream, + ERC20BridgeSource.LiquidityProvider, + ERC20BridgeSource.CryptoCom, + ERC20BridgeSource.Linkswap, + ]), + [ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]), + [ChainId.BSC]: new SourceFilters([ + ERC20BridgeSource.BakerySwap, + ERC20BridgeSource.Belt, + ERC20BridgeSource.Dodo, + ERC20BridgeSource.DodoV2, + ERC20BridgeSource.Ellipsis, + ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.MultiHop, + ERC20BridgeSource.Nerve, + ERC20BridgeSource.PancakeSwap, + ERC20BridgeSource.SushiSwap, + ]), + }, + new SourceFilters([]), +); /** * 0x Protocol Fee Multiplier @@ -100,7 +156,13 @@ export const PROTOCOL_FEE_MULTIPLIER = new BigNumber(70000); /** * Sources to poll for ETH fee price estimates. */ -export const FEE_QUOTE_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2]; +export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2], + [ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap], + }, + [], +); // HACK(mzhu25): Limit and RFQ orders need to be treated as different sources // when computing the exchange proxy gas overhead. @@ -173,7 +235,7 @@ export const TOKENS = { ...MIRROR_WRAPPED_TOKENS, }; -export const POOLS = { +export const CURVE_POOLS = { curve_compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound // 1.USDT is dead curve_PAX: '0x06364f10b501e868329afbc005b3492902d6c763', // 2.PAX @@ -202,21 +264,52 @@ export const POOLS = { curve_aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave }; -export const DEFAULT_INTERMEDIATE_TOKENS = [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC, TOKENS.WBTC]; +export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC, TOKENS.WBTC], + [ChainId.BSC]: [ + '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB + '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD + '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', // DAI + '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC + '0x2170ed0880ac9a755fd29b2688956bd959f933f8', // ETH + '0x55d398326f99059ff775485246999027b3197955', // BUSD-T + ], + }, + [], +); -export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjacencyGraphBuilder({ - default: DEFAULT_INTERMEDIATE_TOKENS, -}) - // Mirror Protocol - .tap(builder => { - builder - .add(TOKENS.MIR, TOKENS.UST) - .add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)]) - .add(TOKENS.USDT, TOKENS.UST); - Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST)); - }) - // Build - .build(); +export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: new TokenAdjacencyGraphBuilder({ + default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Mainnet], + }) + // Mirror Protocol + .tap(builder => { + builder + .add(TOKENS.MIR, TOKENS.UST) + .add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)]) + .add(TOKENS.USDT, TOKENS.UST); + Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST)); + }) + // Build + .build(), + [ChainId.BSC]: new TokenAdjacencyGraphBuilder({ + default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC], + }).build(), + }, + new TokenAdjacencyGraphBuilder({ default: [] }).build(), +); + +export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: TOKENS.WETH, + [ChainId.BSC]: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB + }, + NULL_ADDRESS, +); + +export const NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID = valueByChainId({}, ONE_ETHER); /** * Mainnet Curve configuration @@ -224,147 +317,147 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjac * I.e DaiUsdc curve has DAI as index 0 and USDC as index 1 */ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = { - [POOLS.curve_compound]: { + [CURVE_POOLS.curve_compound]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, - poolAddress: POOLS.curve_compound, + poolAddress: CURVE_POOLS.curve_compound, tokens: [TOKENS.DAI, TOKENS.USDC], metaToken: undefined, }, - [POOLS.curve_PAX]: { + [CURVE_POOLS.curve_PAX]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_PAX, + poolAddress: CURVE_POOLS.curve_PAX, tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT, TOKENS.PAX], metaToken: undefined, }, - [POOLS.curve_sUSD]: { + [CURVE_POOLS.curve_sUSD]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_sUSD, + poolAddress: CURVE_POOLS.curve_sUSD, tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD], metaToken: undefined, }, - [POOLS.curve_renBTC]: { + [CURVE_POOLS.curve_renBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_renBTC, + poolAddress: CURVE_POOLS.curve_renBTC, tokens: [TOKENS.RenBTC, TOKENS.WBTC], metaToken: undefined, }, - [POOLS.curve_sBTC]: { + [CURVE_POOLS.curve_sBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_sBTC, + poolAddress: CURVE_POOLS.curve_sBTC, tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], metaToken: undefined, }, - [POOLS.curve_HBTC]: { + [CURVE_POOLS.curve_HBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_HBTC, + poolAddress: CURVE_POOLS.curve_HBTC, tokens: [TOKENS.hBTC, TOKENS.WBTC], metaToken: undefined, }, - [POOLS.curve_TRI]: { + [CURVE_POOLS.curve_TRI]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_TRI, + poolAddress: CURVE_POOLS.curve_TRI, tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: undefined, }, - [POOLS.curve_GUSD]: { + [CURVE_POOLS.curve_GUSD]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_GUSD, + poolAddress: CURVE_POOLS.curve_GUSD, tokens: [TOKENS.GUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.GUSD, }, - [POOLS.curve_HUSD]: { + [CURVE_POOLS.curve_HUSD]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_HUSD, + poolAddress: CURVE_POOLS.curve_HUSD, tokens: [TOKENS.HUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.HUSD, }, - [POOLS.curve_USDN]: { + [CURVE_POOLS.curve_USDN]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_USDN, + poolAddress: CURVE_POOLS.curve_USDN, tokens: [TOKENS.USDN, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.USDN, }, - [POOLS.curve_mUSD]: { + [CURVE_POOLS.curve_mUSD]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_mUSD, + poolAddress: CURVE_POOLS.curve_mUSD, tokens: [TOKENS.mUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.mUSD, }, - [POOLS.curve_tBTC]: { + [CURVE_POOLS.curve_tBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_tBTC, + poolAddress: CURVE_POOLS.curve_tBTC, tokens: [TOKENS.tBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], metaToken: TOKENS.tBTC, }, - [POOLS.curve_dUSD]: { + [CURVE_POOLS.curve_dUSD]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_dUSD, + poolAddress: CURVE_POOLS.curve_dUSD, tokens: [TOKENS.dUSD, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.dUSD, }, - [POOLS.curve_pBTC]: { + [CURVE_POOLS.curve_pBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_pBTC, + poolAddress: CURVE_POOLS.curve_pBTC, tokens: [TOKENS.pBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], metaToken: TOKENS.pBTC, }, - [POOLS.curve_bBTC]: { + [CURVE_POOLS.curve_bBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_bBTC, + poolAddress: CURVE_POOLS.curve_bBTC, tokens: [TOKENS.bBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], metaToken: TOKENS.bBTC, }, - [POOLS.curve_oBTC]: { + [CURVE_POOLS.curve_oBTC]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_oBTC, + poolAddress: CURVE_POOLS.curve_oBTC, tokens: [TOKENS.oBTC, TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], metaToken: TOKENS.oBTC, }, - [POOLS.curve_UST]: { + [CURVE_POOLS.curve_UST]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_UST, + poolAddress: CURVE_POOLS.curve_UST, tokens: [TOKENS.UST, TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: TOKENS.UST, }, - [POOLS.curve_eurs]: { + [CURVE_POOLS.curve_eurs]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_eurs, + poolAddress: CURVE_POOLS.curve_eurs, tokens: [TOKENS.EURS, TOKENS.sEUR], metaToken: undefined, }, @@ -376,19 +469,19 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = { // tokens: [TOKENS.ETH, TOKENS.sETH], // metaToken: undefined, // }, - [POOLS.curve_aave]: { + [CURVE_POOLS.curve_aave]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_aave, + poolAddress: CURVE_POOLS.curve_aave, tokens: [TOKENS.DAI, TOKENS.USDC, TOKENS.USDT], metaToken: undefined, }, - [POOLS.curve_aave]: { + [CURVE_POOLS.curve_aave]: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, buyQuoteFunctionSelector: CurveFunctionSelectors.None, - poolAddress: POOLS.curve_aave, + poolAddress: CURVE_POOLS.curve_aave, tokens: [TOKENS.aDAI, TOKENS.aUSDC, TOKENS.aUSDT], metaToken: undefined, }, @@ -443,6 +536,52 @@ export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = { }, }; +export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = { + ['0x1b3771a66ee31180906972580ade9b81afc5fcdc']: { + exchangeFunctionSelector: CurveFunctionSelectors.swap, + sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0x1b3771a66ee31180906972580ade9b81afc5fcdc', + tokens: [ + '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD + '0x55d398326f99059ff775485246999027b3197955', // BUSD-T + '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC + ], + metaToken: undefined, + }, +}; + +export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = { + ['0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd']: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd', + tokens: [ + '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', // bDAI + '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC + '0x55d398326f99059ff775485246999027b3197955', // BUSD-T + '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD + ], + metaToken: undefined, + }, +}; + +export const ELLIPSIS_BSC_INFOS: { [name: string]: CurveInfo } = { + ['0x160caed03795365f3a589f10c379ffa7d75d4e76']: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0x160caed03795365f3a589f10c379ffa7d75d4e76', + tokens: [ + '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD + '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC + '0x55d398326f99059ff775485246999027b3197955', // BUSD-T + ], + metaToken: undefined, + }, +}; + /** * Kyber reserve prefixes * 0xff Fed price reserve @@ -451,56 +590,181 @@ export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = { */ export const KYBER_BRIDGED_LIQUIDITY_PREFIX = '0xbb'; export const MAX_KYBER_RESERVES_QUERIED = 5; -export const MAINNET_KYBER_NETWORK_PROXY = '0x9aab3f75489902f3a48495025729a0af77d4b11e'; +export const KYBER_CONFIG_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: { + networkProxy: '0x9aab3f75489902f3a48495025729a0af77d4b11e', + hintHandler: '0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C', + weth: TOKENS.WETH, + }, + }, + { + networkProxy: NULL_ADDRESS, + hintHandler: NULL_ADDRESS, + weth: NULL_ADDRESS, + }, +); export const LIQUIDITY_PROVIDER_REGISTRY: LiquidityProviderRegistry = {}; -export const MAINNET_UNISWAP_V1_ROUTER = '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95'; -export const MAINNET_UNISWAP_V2_ROUTER = '0xf164fc0ec4e93095b804a4795bbe1e041497b92a'; -export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f'; -export const MAINNET_CRYPTO_COM_ROUTER = '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3'; -export const MAINNET_LINKSWAP_ROUTER = '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1'; +export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95', + }, + NULL_ADDRESS, +); -export const MAINNET_MSTABLE_ROUTER = '0xe2f2a5c287993345a840db3b0845fbc70f5935a5'; -export const MAINNET_OASIS_ROUTER = '0x794e6e91555438afc3ccf1c5076a74f42133d08d'; +export const UNISWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a', + }, + NULL_ADDRESS, +); -export const MAINNET_MOONISWAP_REGISTRY = '0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303'; -export const MAINNET_MOONISWAP_V2_REGISTRY = '0xc4a8b7e29e3c8ec560cd4945c1cf3461a85a148d'; -export const MAINNET_MOONISWAP_V2_1_REGISTRY = '0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643'; +export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f', + [ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506', + }, + NULL_ADDRESS, +); -export const MAINNET_DODO_HELPER = '0x533da777aedce766ceae696bf90f8541a4ba80eb'; -export const MAINNET_DODOV2_PRIVATE_POOL_FACTORY = '0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef'; -export const MAINNET_DODOV2_VENDING_MACHINE_FACTORY = '0x72d220ce168c4f361dd4dee5d826a01ad8598f6c'; +export const CRYPTO_COM_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3', + }, + NULL_ADDRESS, +); + +export const LINKSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( + { [ChainId.Mainnet]: '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1' }, + NULL_ADDRESS, +); + +export const MSTABLE_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5', + }, + NULL_ADDRESS, +); + +export const OASIS_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0x5e3e0548935a83ad29fb2a9153d331dc6d49020f', + }, + NULL_ADDRESS, +); + +export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: [ + '0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303', + '0xc4a8b7e29e3c8ec560cd4945c1cf3461a85a148d', + '0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643', + ], + [ChainId.BSC]: ['0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64'], + }, + [] as string[], +); + +export const DODO_CONFIG_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: { + helper: '0x533da777aedce766ceae696bf90f8541a4ba80eb', + registry: '0x3A97247DF274a17C59A3bd12735ea3FcDFb49950', + }, + [ChainId.BSC]: { + helper: '0x0f859706aee7fcf61d5a8939e8cb9dbb6c1eda33', + registry: '0xca459456a45e300aa7ef447dbb60f87cccb42828', + }, + }, + { helper: NULL_ADDRESS, registry: NULL_ADDRESS }, +); + +export const DODOV2_FACTORIES_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: [ + '0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef', // Private Pool + '0x72d220ce168c4f361dd4dee5d826a01ad8598f6c', // Vending Machine + ], + [ChainId.BSC]: [ + '0xafe0a75dffb395eaabd0a7e1bbbd0b11f8609eef', // Private Pool + '0x790b4a80fb1094589a3c0efc8740aa9b0c1733fb', // Vending Machine + ], + }, + [] as string[], +); export const MAX_DODOV2_POOLS_QUERIED = 3; -export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID: { [id: string]: string } = { - '1': '0x561b94454b65614ae3db0897b74303f4acf7cc75', - '3': '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2', - '1337': NULL_ADDRESS, -}; - -// TODO(dorothy-zbornak): Point these to real addresses after deploying. -export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID: { [id: string]: string } = { - '1': '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889', - '3': '0x87e0393aee0fb8c10b8653c6507c182264fe5a34', - '1337': NULL_ADDRESS, -}; - -export const MAINNET_SHELL_POOLS = { - StableCoins: { - poolAddress: '0x8f26d7bab7a73309141a291525c965ecdea7bf42', - tokens: [TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD, TOKENS.DAI], +export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0x7a6F6a048fE2Dc1397ABa0bf7879d3eacF371C53', + [ChainId.Ropsten]: '0xAa213dcDFbF104e08cbAeC3d1628eD197553AfCc', }, - Bitcoin: { - poolAddress: '0xc2d019b901f8d4fdb2b9a65b5d226ad88c66ee8d', - tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], + NULL_ADDRESS, +); + +export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889', + [ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34', }, -}; + NULL_ADDRESS, +); + +export const BANCOR_REGISTRY_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: '0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4', + }, + NULL_ADDRESS, +); + +export const SHELL_POOLS_BY_CHAIN_ID = valueByChainId( + { + [ChainId.Mainnet]: { + StableCoins: { + poolAddress: '0x8f26d7bab7a73309141a291525c965ecdea7bf42', + tokens: [TOKENS.USDC, TOKENS.USDT, TOKENS.sUSD, TOKENS.DAI], + }, + Bitcoin: { + poolAddress: '0xc2d019b901f8d4fdb2b9a65b5d226ad88c66ee8d', + tokens: [TOKENS.RenBTC, TOKENS.WBTC, TOKENS.sBTC], + }, + }, + }, + { + StableCoins: { + poolAddress: NULL_ADDRESS, + tokens: [] as string[], + }, + Bitcoin: { + poolAddress: NULL_ADDRESS, + tokens: [] as string[], + }, + }, +); export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer'; export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_MAX_POOLS_FETCHED = 3; +// +// BSC +// +export const PANCAKESWAP_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.BSC]: '0x05ff2b0db69458a0750badebc4f9e13add608c7f', + }, + NULL_ADDRESS, +); + +export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId( + { + [ChainId.BSC]: '0xcde540d7eafe93ac5fe6233bee57e1270d3e330f', + }, + NULL_ADDRESS, +); + /** * Calculated gross gas cost of the underlying exchange. * The cost of switching from one source to another, assuming @@ -527,32 +791,32 @@ export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.Curve]: fillData => { const poolAddress = (fillData as CurveFillData).pool.poolAddress.toLowerCase(); switch (poolAddress) { - case POOLS.curve_renBTC: - case POOLS.curve_sBTC: - case POOLS.curve_sUSD: - case POOLS.curve_HBTC: - case POOLS.curve_TRI: + case CURVE_POOLS.curve_renBTC: + case CURVE_POOLS.curve_sBTC: + case CURVE_POOLS.curve_sUSD: + case CURVE_POOLS.curve_HBTC: + case CURVE_POOLS.curve_TRI: return 150e3; - case POOLS.curve_USDN: - case POOLS.curve_mUSD: + case CURVE_POOLS.curve_USDN: + case CURVE_POOLS.curve_mUSD: return 300e3; - case POOLS.curve_GUSD: - case POOLS.curve_HUSD: + case CURVE_POOLS.curve_GUSD: + case CURVE_POOLS.curve_HUSD: return 310e3; - case POOLS.curve_tBTC: + case CURVE_POOLS.curve_tBTC: return 370e3; - case POOLS.curve_UST: + case CURVE_POOLS.curve_UST: return 500e3; - case POOLS.curve_dUSD: - case POOLS.curve_bBTC: - case POOLS.curve_oBTC: - case POOLS.curve_eurs: + case CURVE_POOLS.curve_dUSD: + case CURVE_POOLS.curve_bBTC: + case CURVE_POOLS.curve_oBTC: + case CURVE_POOLS.curve_eurs: return 600e3; - case POOLS.curve_compound: + case CURVE_POOLS.curve_compound: return 750e3; - case POOLS.curve_aave: + case CURVE_POOLS.curve_aave: return 800e3; - case POOLS.curve_PAX: + case CURVE_POOLS.curve_PAX: return 850e3; // case POOLS.curve_seth: default: @@ -572,7 +836,7 @@ export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.SushiSwap]: (fillData?: FillData) => { // TODO: Different base cost if to/from ETH. let gas = 90e3; - const path = (fillData as SushiSwapFillData).tokenAddressPath; + const path = (fillData as UniswapV2FillData).tokenAddressPath; if (path.length > 2) { gas += (path.length - 2) * 60e3; // +60k for each hop. } @@ -601,6 +865,7 @@ export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.MStable]: () => 700e3, [ERC20BridgeSource.Mooniswap]: () => 130e3, [ERC20BridgeSource.Swerve]: () => 150e3, + [ERC20BridgeSource.Nerve]: () => 150e3, [ERC20BridgeSource.Shell]: () => 170e3, [ERC20BridgeSource.MultiHop]: (fillData?: FillData) => { const firstHop = (fillData as MultiHopFillData).firstHopSource; @@ -620,7 +885,7 @@ export const DEFAULT_GAS_SCHEDULE: Required = { }, [ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3, [ERC20BridgeSource.SnowSwap]: fillData => { - switch ((fillData as SnowSwapFillData).pool.poolAddress.toLowerCase()) { + switch ((fillData as CurveFillData).pool.poolAddress.toLowerCase()) { case '0xbf7ccd6c446acfcc5df023043f2167b62e81899b': return 1000e3; case '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b': @@ -637,6 +902,29 @@ export const DEFAULT_GAS_SCHEDULE: Required = { } return gas; }, + // + // BSC + // + [ERC20BridgeSource.PancakeSwap]: (fillData?: FillData) => { + // TODO: Different base cost if to/from ETH. + let gas = 90e3; + const path = (fillData as UniswapV2FillData).tokenAddressPath; + if (path.length > 2) { + gas += (path.length - 2) * 60e3; // +60k for each hop. + } + return gas; + }, + [ERC20BridgeSource.BakerySwap]: (fillData?: FillData) => { + // TODO: Different base cost if to/from ETH. + let gas = 90e3; + const path = (fillData as UniswapV2FillData).tokenAddressPath; + if (path.length > 2) { + gas += (path.length - 2) * 60e3; // +60k for each hop. + } + return gas; + }, + [ERC20BridgeSource.Belt]: () => 4500e3, + [ERC20BridgeSource.Ellipsis]: () => 150e3, }; export const DEFAULT_FEE_SCHEDULE: Required = { ...DEFAULT_GAS_SCHEDULE }; 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 b6edbbfd77..8642ce0c62 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -19,11 +19,12 @@ import { import { generateQuoteReport, QuoteReport } from './../quote_report_generator'; import { getComparisonPrices } from './comparison_price'; import { - BUY_SOURCE_FILTER, + BUY_SOURCE_FILTER_BY_CHAIN_ID, DEFAULT_GET_MARKET_ORDERS_OPTS, - FEE_QUOTE_SOURCES, - ONE_ETHER, - SELL_SOURCE_FILTER, + FEE_QUOTE_SOURCES_BY_CHAIN_ID, + NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID, + NATIVE_FEE_TOKEN_BY_CHAIN_ID, + SELL_SOURCE_FILTER_BY_CHAIN_ID, SOURCE_FLAGS, ZERO_AMOUNT, } from './constants'; @@ -50,10 +51,11 @@ import { // tslint:disable:boolean-naming export class MarketOperationUtils { - private readonly _wethAddress: string; private readonly _sellSources: SourceFilters; private readonly _buySources: SourceFilters; - private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES); + private readonly _feeSources: SourceFilters; + private readonly _nativeFeeToken: string; + private readonly _nativeFeeTokenAmount: BigNumber; private static _computeQuoteReport( quoteRequestor: QuoteRequestor | undefined, @@ -71,9 +73,11 @@ export class MarketOperationUtils { private readonly contractAddresses: AssetSwapperContractAddresses, private readonly _orderDomain: OrderDomain, ) { - this._wethAddress = contractAddresses.etherToken.toLowerCase(); - this._buySources = BUY_SOURCE_FILTER; - this._sellSources = SELL_SOURCE_FILTER; + this._buySources = BUY_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId]; + this._sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId]; + this._feeSources = new SourceFilters(FEE_QUOTE_SOURCES_BY_CHAIN_ID[_sampler.chainId]); + this._nativeFeeToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[_sampler.chainId]; + this._nativeFeeTokenAmount = NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID[_sampler.chainId]; } /** @@ -128,9 +132,19 @@ export class MarketOperationUtils { // Get native order fillable amounts. this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy), // Get ETH -> maker token price. - this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER), + this._sampler.getMedianSellRate( + feeSourceFilters.sources, + makerToken, + this._nativeFeeToken, + this._nativeFeeTokenAmount, + ), // Get ETH -> taker token price. - this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER), + this._sampler.getMedianSellRate( + feeSourceFilters.sources, + takerToken, + this._nativeFeeToken, + this._nativeFeeTokenAmount, + ), // Get sell quotes for taker -> maker. this._sampler.getSellQuotes( quoteSourceFilters.exclude(offChainSources).sources, @@ -254,9 +268,19 @@ export class MarketOperationUtils { // Get native order fillable amounts. this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy), // Get ETH -> makerToken token price. - this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER), + this._sampler.getMedianSellRate( + feeSourceFilters.sources, + makerToken, + this._nativeFeeToken, + this._nativeFeeTokenAmount, + ), // Get ETH -> taker token price. - this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER), + this._sampler.getMedianSellRate( + feeSourceFilters.sources, + takerToken, + this._nativeFeeToken, + this._nativeFeeTokenAmount, + ), // Get buy quotes for taker -> maker. this._sampler.getBuyQuotes( quoteSourceFilters.exclude(offChainSources).sources, @@ -362,8 +386,8 @@ export class MarketOperationUtils { this._sampler.getMedianSellRate( feeSourceFilters.sources, orders[0].order.takerToken, - this._wethAddress, - ONE_ETHER, + this._nativeFeeToken, + this._nativeFeeTokenAmount, ), ), ...batchNativeOrders.map((orders, i) => 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 39c972be85..54ed12fb06 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -1,17 +1,9 @@ -import { BridgeSource, FillQuoteTransformerOrderType } from '@0x/protocol-utils'; +import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils'; import { AbiEncoder, BigNumber } from '@0x/utils'; import { AssetSwapperContractAddresses, MarketOperation } from '../../types'; -import { - MAINNET_DODO_HELPER, - MAINNET_KYBER_NETWORK_PROXY, - MAINNET_MSTABLE_ROUTER, - MAINNET_OASIS_ROUTER, - MAINNET_UNISWAP_V1_ROUTER, - MAX_UINT256, - ZERO_AMOUNT, -} from './constants'; +import { MAX_UINT256, ZERO_AMOUNT } from './constants'; import { AggregationError, BalancerFillData, @@ -21,6 +13,7 @@ import { DexSample, DODOFillData, ERC20BridgeSource, + GenericRouterFillData, KyberFillData, LiquidityProviderFillData, MooniswapFillData, @@ -33,9 +26,6 @@ import { OptimizedMarketOrderBase, OrderDomain, ShellFillData, - SnowSwapFillData, - SushiSwapFillData, - SwerveFillData, UniswapV2FillData, } from './types'; @@ -80,48 +70,59 @@ export function createOrdersFromTwoHopSample( ]; } -export function getERC20BridgeSourceToBridgeSource(source: ERC20BridgeSource): BridgeSource { +export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): string { switch (source) { case ERC20BridgeSource.Balancer: - return BridgeSource.Balancer; + return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer'); case ERC20BridgeSource.Bancor: - return BridgeSource.Bancor; + return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor'); // case ERC20BridgeSource.CoFiX: - // return BridgeSource.CoFiX; + // return encodeBridgeSourceId(BridgeProtocol.CoFiX, 'CoFiX'); case ERC20BridgeSource.Curve: - return BridgeSource.Curve; + return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve'); case ERC20BridgeSource.Cream: - return BridgeSource.Cream; + return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Cream'); case ERC20BridgeSource.CryptoCom: - return BridgeSource.CryptoCom; + return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom'); case ERC20BridgeSource.Dodo: - return BridgeSource.Dodo; + return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo'); case ERC20BridgeSource.Kyber: - return BridgeSource.Kyber; + return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber'); case ERC20BridgeSource.LiquidityProvider: - return BridgeSource.LiquidityProvider; + // "LiquidityProvider" is too long to encode (17 characters). + return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP'); case ERC20BridgeSource.Mooniswap: - return BridgeSource.Mooniswap; + return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap'); case ERC20BridgeSource.MStable: - return BridgeSource.MStable; + return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable'); case ERC20BridgeSource.Eth2Dai: - return BridgeSource.Oasis; + return encodeBridgeSourceId(BridgeProtocol.Oasis, 'Eth2Dai'); case ERC20BridgeSource.Shell: - return BridgeSource.Shell; + return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell'); case ERC20BridgeSource.SnowSwap: - return BridgeSource.Snowswap; + return encodeBridgeSourceId(BridgeProtocol.Curve, 'SnowSwap'); case ERC20BridgeSource.SushiSwap: - return BridgeSource.Sushiswap; + return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap'); case ERC20BridgeSource.Swerve: - return BridgeSource.Swerve; + return encodeBridgeSourceId(BridgeProtocol.Curve, 'Swerve'); case ERC20BridgeSource.Uniswap: - return BridgeSource.Uniswap; + return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap'); case ERC20BridgeSource.UniswapV2: - return BridgeSource.UniswapV2; + return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2'); case ERC20BridgeSource.DodoV2: - return BridgeSource.DodoV2; + return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2'); case ERC20BridgeSource.Linkswap: - return BridgeSource.Linkswap; + return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Linkswap'); + case ERC20BridgeSource.PancakeSwap: + return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap'); + case ERC20BridgeSource.BakerySwap: + return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BakerySwap'); + case ERC20BridgeSource.Nerve: + return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Nerve'); + case ERC20BridgeSource.Belt: + return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt'); + case ERC20BridgeSource.Ellipsis: + return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis'); default: throw new Error(AggregationError.NoBridgeForSource); } @@ -146,9 +147,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder case ERC20BridgeSource.Curve: case ERC20BridgeSource.Swerve: case ERC20BridgeSource.SnowSwap: - const curveFillData = (order as OptimizedMarketBridgeOrder< - CurveFillData | SwerveFillData | SnowSwapFillData - >).fillData; + case ERC20BridgeSource.Nerve: + case ERC20BridgeSource.Belt: + case ERC20BridgeSource.Ellipsis: + const curveFillData = (order as OptimizedMarketBridgeOrder).fillData; bridgeData = encoder.encode([ curveFillData.pool.poolAddress, curveFillData.pool.exchangeFunctionSelector, @@ -169,13 +171,14 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder case ERC20BridgeSource.SushiSwap: case ERC20BridgeSource.CryptoCom: case ERC20BridgeSource.Linkswap: - const uniswapV2FillData = (order as OptimizedMarketBridgeOrder) - .fillData; + case ERC20BridgeSource.PancakeSwap: + case ERC20BridgeSource.BakerySwap: + const uniswapV2FillData = (order as OptimizedMarketBridgeOrder).fillData; bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]); break; case ERC20BridgeSource.Kyber: const kyberFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([MAINNET_KYBER_NETWORK_PROXY, kyberFillData.hint]); + bridgeData = encoder.encode([kyberFillData.networkProxy, kyberFillData.hint]); break; case ERC20BridgeSource.Mooniswap: const mooniswapFillData = (order as OptimizedMarketBridgeOrder).fillData; @@ -183,7 +186,11 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder break; case ERC20BridgeSource.Dodo: const dodoFillData = (order as OptimizedMarketBridgeOrder).fillData; - bridgeData = encoder.encode([MAINNET_DODO_HELPER, dodoFillData.poolAddress, dodoFillData.isSellBase]); + bridgeData = encoder.encode([ + dodoFillData.helperAddress, + dodoFillData.poolAddress, + dodoFillData.isSellBase, + ]); break; case ERC20BridgeSource.DodoV2: const dodoV2FillData = (order as OptimizedMarketBridgeOrder).fillData; @@ -198,13 +205,16 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder bridgeData = encoder.encode([lpFillData.poolAddress, tokenAddressEncoder.encode([order.takerToken])]); break; case ERC20BridgeSource.Uniswap: - bridgeData = encoder.encode([MAINNET_UNISWAP_V1_ROUTER]); + const uniFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([uniFillData.router]); break; case ERC20BridgeSource.Eth2Dai: - bridgeData = encoder.encode([MAINNET_OASIS_ROUTER]); + const oasisFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([oasisFillData.router]); break; case ERC20BridgeSource.MStable: - bridgeData = encoder.encode([MAINNET_MSTABLE_ROUTER]); + const mStableFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([mStableFillData.router]); break; default: throw new Error(AggregationError.NoBridgeForSource); @@ -275,6 +285,9 @@ export const BRIDGE_ENCODERS: { [ERC20BridgeSource.Curve]: curveEncoder, [ERC20BridgeSource.Swerve]: curveEncoder, [ERC20BridgeSource.SnowSwap]: curveEncoder, + [ERC20BridgeSource.Nerve]: curveEncoder, + [ERC20BridgeSource.Belt]: curveEncoder, + [ERC20BridgeSource.Ellipsis]: curveEncoder, // UniswapV2 like, (router, address[]) [ERC20BridgeSource.Bancor]: routerAddressPathEncoder, [ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder, @@ -289,6 +302,9 @@ export const BRIDGE_ENCODERS: { [ERC20BridgeSource.Balancer]: poolEncoder, [ERC20BridgeSource.Cream]: poolEncoder, [ERC20BridgeSource.Uniswap]: poolEncoder, + // BSC + [ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder, + [ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder, }; function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] { 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 6bc96337ed..16dd060904 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -1,3 +1,4 @@ +import { ChainId } from '@0x/contract-addresses'; import { BigNumber, NULL_BYTES } from '@0x/utils'; import { SamplerOverrides } from '../../types'; @@ -33,6 +34,7 @@ type BatchedOperationResult = T extends BatchedOperation ? TRe */ export class DexOrderSampler extends SamplerOperations { constructor( + public readonly chainId: ChainId, _samplerContract: ERC20BridgeSamplerContract, private readonly _samplerOverrides?: SamplerOverrides, balancerPoolsCache?: BalancerPoolsCache, @@ -42,6 +44,7 @@ export class DexOrderSampler extends SamplerOperations { bancorServiceFn: () => Promise = async () => undefined, ) { super( + chainId, _samplerContract, balancerPoolsCache, creamPoolsCache, 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 2757e8fa51..f727e498b9 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,3 +1,4 @@ +import { ChainId } from '@0x/contract-addresses'; import { LimitOrderFields } from '@0x/protocol-utils'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -8,33 +9,38 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache } from './balancer_utils'; import { BancorService } from './bancor_service'; import { - getCurveInfosForPair, + getCurveLikeInfosForPair, getDodoV2Offsets, getKyberOffsets, - getSnowSwapInfosForPair, - getSwerveInfosForPair, + getShellsForPair, isAllowedKyberReserveId, + isBadTokenForSource, + isValidAddress, + uniswapV2LikeRouterAddress, } from './bridge_source_utils'; import { + BANCOR_REGISTRY_BY_CHAIN_ID, + DODO_CONFIG_BY_CHAIN_ID, + DODOV2_FACTORIES_BY_CHAIN_ID, + KYBER_CONFIG_BY_CHAIN_ID, + LINKSWAP_ROUTER_BY_CHAIN_ID, LIQUIDITY_PROVIDER_REGISTRY, - MAINNET_CRYPTO_COM_ROUTER, - MAINNET_DODOV2_PRIVATE_POOL_FACTORY, - MAINNET_DODOV2_VENDING_MACHINE_FACTORY, - MAINNET_LINKSWAP_ROUTER, - MAINNET_MOONISWAP_REGISTRY, - MAINNET_MOONISWAP_V2_1_REGISTRY, - MAINNET_MOONISWAP_V2_REGISTRY, - MAINNET_SUSHI_SWAP_ROUTER, - MAINNET_UNISWAP_V2_ROUTER, MAX_UINT256, + MOONISWAP_REGISTRIES_BY_CHAIN_ID, + MSTABLE_ROUTER_BY_CHAIN_ID, + NATIVE_FEE_TOKEN_BY_CHAIN_ID, + NULL_ADDRESS, + NULL_BYTES, + OASIS_ROUTER_BY_CHAIN_ID, + SELL_SOURCE_FILTER_BY_CHAIN_ID, TOKENS, + UNISWAPV1_ROUTER_BY_CHAIN_ID, ZERO_AMOUNT, } from './constants'; import { CreamPoolsCache } from './cream_utils'; import { getLiquidityProvidersForPair } from './liquidity_provider_utils'; import { getIntermediateTokens } from './multihop_utils'; import { SamplerContractOperation } from './sampler_contract_operation'; -import { getShellsForPair } from './shell_utils'; import { SourceFilters } from './source_filters'; import { BalancerFillData, @@ -45,19 +51,16 @@ import { DexSample, DODOFillData, ERC20BridgeSource, + GenericRouterFillData, HopInfo, KyberFillData, + KyberSamplerOpts, LiquidityProviderFillData, LiquidityProviderRegistry, MooniswapFillData, MultiHopFillData, ShellFillData, - SnowSwapFillData, - SnowSwapInfo, SourceQuoteOperation, - SushiSwapFillData, - SwerveFillData, - SwerveInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './types'; @@ -91,6 +94,7 @@ export class SamplerOperations { } constructor( + public readonly chainId: ChainId, protected readonly _samplerContract: ERC20BridgeSamplerContract, public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(), @@ -152,6 +156,7 @@ export class SamplerOperations { } public getKyberSellQuotes( + kyberOpts: KyberSamplerOpts, reserveOffset: BigNumber, makerToken: string, takerToken: string, @@ -161,19 +166,21 @@ export class SamplerOperations { source: ERC20BridgeSource.Kyber, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromKyberNetwork, - params: [reserveOffset, takerToken, makerToken, takerFillAmounts], + params: [{ ...kyberOpts, reserveOffset, hint: NULL_BYTES }, takerToken, makerToken, takerFillAmounts], callback: (callResults: string, fillData: KyberFillData): BigNumber[] => { const [reserveId, hint, samples] = this._samplerContract.getABIDecodedReturnData< [string, string, BigNumber[]] >('sampleSellsFromKyberNetwork', callResults); fillData.hint = hint; fillData.reserveId = reserveId; + fillData.networkProxy = kyberOpts.networkProxy; return isAllowedKyberReserveId(reserveId) ? samples : []; }, }); } public getKyberBuyQuotes( + kyberOpts: KyberSamplerOpts, reserveOffset: BigNumber, makerToken: string, takerToken: string, @@ -183,41 +190,52 @@ export class SamplerOperations { source: ERC20BridgeSource.Kyber, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromKyberNetwork, - params: [reserveOffset, takerToken, makerToken, makerFillAmounts], + params: [{ ...kyberOpts, reserveOffset, hint: NULL_BYTES }, takerToken, makerToken, makerFillAmounts], callback: (callResults: string, fillData: KyberFillData): BigNumber[] => { const [reserveId, hint, samples] = this._samplerContract.getABIDecodedReturnData< [string, string, BigNumber[]] >('sampleBuysFromKyberNetwork', callResults); fillData.hint = hint; fillData.reserveId = reserveId; + fillData.networkProxy = kyberOpts.networkProxy; return isAllowedKyberReserveId(reserveId) ? samples : []; }, }); } public getUniswapSellQuotes( + router: string, makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { + // Uniswap uses ETH instead of WETH, represented by address(0) + const uniswapTakerToken = takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + const uniswapMakerToken = makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; return new SamplerContractOperation({ source: ERC20BridgeSource.Uniswap, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromUniswap, - params: [takerToken, makerToken, takerFillAmounts], + params: [router, uniswapTakerToken, uniswapMakerToken, takerFillAmounts], }); } public getUniswapBuyQuotes( + router: string, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { + // Uniswap uses ETH instead of WETH, represented by address(0) + const uniswapTakerToken = takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + const uniswapMakerToken = makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; return new SamplerContractOperation({ source: ERC20BridgeSource.Uniswap, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromUniswap, - params: [takerToken, makerToken, makerFillAmounts], + params: [router, uniswapTakerToken, uniswapMakerToken, makerFillAmounts], }); } @@ -288,28 +306,32 @@ export class SamplerOperations { } public getEth2DaiSellQuotes( + router: string, makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { return new SamplerContractOperation({ source: ERC20BridgeSource.Eth2Dai, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromEth2Dai, - params: [takerToken, makerToken, takerFillAmounts], + params: [router, takerToken, makerToken, takerFillAmounts], }); } public getEth2DaiBuyQuotes( + router: string, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { return new SamplerContractOperation({ source: ERC20BridgeSource.Eth2Dai, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromEth2Dai, - params: [takerToken, makerToken, makerFillAmounts], + params: [router, takerToken, makerToken, makerFillAmounts], }); } @@ -318,9 +340,10 @@ export class SamplerOperations { fromTokenIdx: number, toTokenIdx: number, takerFillAmounts: BigNumber[], + source: ERC20BridgeSource = ERC20BridgeSource.Curve, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.Curve, + source, fillData: { pool, fromTokenIdx, @@ -346,121 +369,10 @@ export class SamplerOperations { fromTokenIdx: number, toTokenIdx: number, makerFillAmounts: BigNumber[], + source: ERC20BridgeSource = ERC20BridgeSource.Curve, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.Curve, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - makerFillAmounts, - ], - }); - } - - public getSwerveSellQuotes( - pool: SwerveInfo, - fromTokenIdx: number, - toTokenIdx: number, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Swerve, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - takerFillAmounts, - ], - }); - } - - public getSwerveBuyQuotes( - pool: SwerveInfo, - fromTokenIdx: number, - toTokenIdx: number, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.Swerve, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - makerFillAmounts, - ], - }); - } - - public getSnowSwapSellQuotes( - pool: SnowSwapInfo, - fromTokenIdx: number, - toTokenIdx: number, - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.SnowSwap, - fillData: { - pool, - fromTokenIdx, - toTokenIdx, - }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromCurve, - params: [ - { - poolAddress: pool.poolAddress, - sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, - }, - new BigNumber(fromTokenIdx), - new BigNumber(toTokenIdx), - takerFillAmounts, - ], - }); - } - - public getSnowSwapBuyQuotes( - pool: SnowSwapInfo, - fromTokenIdx: number, - toTokenIdx: number, - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.SnowSwap, + source, fillData: { pool, fromTokenIdx, @@ -587,32 +499,37 @@ export class SamplerOperations { } public getMStableSellQuotes( + router: string, makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { return new SamplerContractOperation({ source: ERC20BridgeSource.MStable, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromMStable, - params: [takerToken, makerToken, takerFillAmounts], + params: [router, takerToken, makerToken, takerFillAmounts], }); } public getMStableBuyQuotes( + router: string, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { + ): SourceQuoteOperation { return new SamplerContractOperation({ source: ERC20BridgeSource.MStable, + fillData: { router }, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromMStable, - params: [takerToken, makerToken, makerFillAmounts], + params: [router, takerToken, makerToken, makerFillAmounts], }); } public getBancorSellQuotes( + registry: string, makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], @@ -622,7 +539,7 @@ export class SamplerOperations { source: ERC20BridgeSource.Bancor, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromBancor, - params: [paths, takerToken, makerToken, takerFillAmounts], + params: [{ registry, paths }, takerToken, makerToken, takerFillAmounts], callback: (callResults: string, fillData: BancorFillData): BigNumber[] => { const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData< [string, string[], BigNumber[]] @@ -636,6 +553,7 @@ export class SamplerOperations { // Unimplemented public getBancorBuyQuotes( + registry: string, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], @@ -644,11 +562,11 @@ export class SamplerOperations { source: ERC20BridgeSource.Bancor, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromBancor, - params: [[], takerToken, makerToken, makerFillAmounts], + params: [{ registry, paths: [] }, takerToken, makerToken, makerFillAmounts], callback: (callResults: string, fillData: BancorFillData): BigNumber[] => { const [networkAddress, path, samples] = this._samplerContract.getABIDecodedReturnData< [string, string[], BigNumber[]] - >('sampleSellsFromBancor', callResults); + >('sampleBuysFromBancor', callResults); fillData.networkAddress = networkAddress; fillData.path = path; return samples; @@ -662,11 +580,16 @@ export class SamplerOperations { takerToken: string, takerFillAmounts: BigNumber[], ): SourceQuoteOperation { + // Mooniswap uses ETH instead of WETH, represented by address(0) + const mooniswapTakerToken = + takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + const mooniswapMakerToken = + makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; return new SamplerContractOperation({ source: ERC20BridgeSource.Mooniswap, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromMooniswap, - params: [registry, takerToken, makerToken, takerFillAmounts], + params: [registry, mooniswapTakerToken, mooniswapMakerToken, takerFillAmounts], callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => { const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( 'sampleSellsFromMooniswap', @@ -684,11 +607,16 @@ export class SamplerOperations { takerToken: string, makerFillAmounts: BigNumber[], ): SourceQuoteOperation { + // Mooniswap uses ETH instead of WETH, represented by address(0) + const mooniswapTakerToken = + takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + const mooniswapMakerToken = + makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; return new SamplerContractOperation({ source: ERC20BridgeSource.Mooniswap, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromMooniswap, - params: [registry, takerToken, makerToken, makerFillAmounts], + params: [registry, mooniswapTakerToken, mooniswapMakerToken, makerFillAmounts], callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => { const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>( 'sampleBuysFromMooniswap', @@ -807,32 +735,6 @@ export class SamplerOperations { ); } - public getSushiSwapSellQuotes( - tokenAddressPath: string[], - takerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.SushiSwap, - fillData: { tokenAddressPath, router: MAINNET_SUSHI_SWAP_ROUTER }, - contract: this._samplerContract, - function: this._samplerContract.sampleSellsFromSushiSwap, - params: [MAINNET_SUSHI_SWAP_ROUTER, tokenAddressPath, takerFillAmounts], - }); - } - - public getSushiSwapBuyQuotes( - tokenAddressPath: string[], - makerFillAmounts: BigNumber[], - ): SourceQuoteOperation { - return new SamplerContractOperation({ - source: ERC20BridgeSource.SushiSwap, - fillData: { tokenAddressPath, router: MAINNET_SUSHI_SWAP_ROUTER }, - contract: this._samplerContract, - function: this._samplerContract.sampleBuysFromSushiSwap, - params: [MAINNET_SUSHI_SWAP_ROUTER, tokenAddressPath, makerFillAmounts], - }); - } - public getShellSellQuotes( poolAddress: string, makerToken: string, @@ -864,6 +766,7 @@ export class SamplerOperations { } public getDODOSellQuotes( + opts: { registry: string; helper: string }, makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], @@ -872,19 +775,21 @@ export class SamplerOperations { source: ERC20BridgeSource.Dodo, contract: this._samplerContract, function: this._samplerContract.sampleSellsFromDODO, - params: [takerToken, makerToken, takerFillAmounts], + params: [opts, takerToken, makerToken, takerFillAmounts], callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< [boolean, string, BigNumber[]] >('sampleSellsFromDODO', callResults); fillData.isSellBase = isSellBase; fillData.poolAddress = pool; + fillData.helperAddress = opts.helper; return samples; }, }); } public getDODOBuyQuotes( + opts: { registry: string; helper: string }, makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], @@ -893,13 +798,14 @@ export class SamplerOperations { source: ERC20BridgeSource.Dodo, contract: this._samplerContract, function: this._samplerContract.sampleBuysFromDODO, - params: [takerToken, makerToken, makerFillAmounts], + params: [opts, takerToken, makerToken, makerFillAmounts], callback: (callResults: string, fillData: DODOFillData): BigNumber[] => { const [isSellBase, pool, samples] = this._samplerContract.getABIDecodedReturnData< [boolean, string, BigNumber[]] >('sampleBuysFromDODO', callResults); fillData.isSellBase = isSellBase; fillData.poolAddress = pool; + fillData.helperAddress = opts.helper; return samples; }, }); @@ -1039,90 +945,71 @@ export class SamplerOperations { // Find the adjacent tokens in the provided tooken adjacency graph, // e.g if this is DAI->USDC we may check for DAI->WETH->USDC const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph); - const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources); - return _.flatten( + // Drop out MultiHop and Native as we do not query those here. + const _sources = SELL_SOURCE_FILTER_BY_CHAIN_ID[this.chainId] + .exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]) + .getAllowed(sources); + const allOps = _.flatten( _sources.map( (source): SourceQuoteOperation | SourceQuoteOperation[] => { + if (isBadTokenForSource(makerToken, source) || isBadTokenForSource(takerToken, source)) { + return []; + } switch (source) { case ERC20BridgeSource.Eth2Dai: - return this.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts); + return isValidAddress(OASIS_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getEth2DaiSellQuotes( + OASIS_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + takerFillAmounts, + ) + : []; case ERC20BridgeSource.Uniswap: - return this.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts); + return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getUniswapSellQuotes( + UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + takerFillAmounts, + ) + : []; case ERC20BridgeSource.UniswapV2: - const ops = [ - this.getUniswapV2SellQuotes( - MAINNET_UNISWAP_V2_ROUTER, - [takerToken, makerToken], - takerFillAmounts, - ), - ]; - intermediateTokens.forEach(t => { - ops.push( - this.getUniswapV2SellQuotes( - MAINNET_UNISWAP_V2_ROUTER, - [takerToken, t, makerToken], - takerFillAmounts, - ), - ); - }); - return ops; case ERC20BridgeSource.SushiSwap: - const sushiOps = [this.getSushiSwapSellQuotes([takerToken, makerToken], takerFillAmounts)]; - intermediateTokens.forEach(t => { - sushiOps.push( - this.getSushiSwapSellQuotes([takerToken, t, makerToken], takerFillAmounts), - ); - }); - return sushiOps; case ERC20BridgeSource.CryptoCom: - const cryptoComOps = [ - this.getUniswapV2SellQuotes( - MAINNET_CRYPTO_COM_ROUTER, - [takerToken, makerToken], - takerFillAmounts, - ERC20BridgeSource.CryptoCom, - ), - ]; - intermediateTokens.forEach(t => { - cryptoComOps.push( - this.getUniswapV2SellQuotes( - MAINNET_CRYPTO_COM_ROUTER, - [takerToken, t, makerToken], - takerFillAmounts, - ERC20BridgeSource.CryptoCom, - ), - ); - }); - return cryptoComOps; + case ERC20BridgeSource.PancakeSwap: + case ERC20BridgeSource.BakerySwap: + const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source); + if (!isValidAddress(uniLikeRouter)) { + return []; + } + return [ + [takerToken, makerToken], + ...intermediateTokens.map(t => [takerToken, t, makerToken]), + ].map(path => this.getUniswapV2SellQuotes(uniLikeRouter, path, takerFillAmounts, source)); case ERC20BridgeSource.Kyber: return getKyberOffsets().map(offset => - this.getKyberSellQuotes(offset, makerToken, takerToken, takerFillAmounts), + this.getKyberSellQuotes( + KYBER_CONFIG_BY_CHAIN_ID[this.chainId], + offset, + makerToken, + takerToken, + takerFillAmounts, + ), ); case ERC20BridgeSource.Curve: - return getCurveInfosForPair(takerToken, makerToken).map(pool => + case ERC20BridgeSource.Swerve: + case ERC20BridgeSource.SnowSwap: + case ERC20BridgeSource.Nerve: + case ERC20BridgeSource.Belt: + case ERC20BridgeSource.Ellipsis: + return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => this.getCurveSellQuotes( pool, pool.tokens.indexOf(takerToken), pool.tokens.indexOf(makerToken), takerFillAmounts, - ), - ); - case ERC20BridgeSource.Swerve: - return getSwerveInfosForPair(takerToken, makerToken).map(pool => - this.getSwerveSellQuotes( - pool, - pool.tokens.indexOf(takerToken), - pool.tokens.indexOf(makerToken), - takerFillAmounts, - ), - ); - case ERC20BridgeSource.SnowSwap: - return getSnowSwapInfosForPair(takerToken, makerToken).map(pool => - this.getSnowSwapSellQuotes( - pool, - pool.tokens.indexOf(takerToken), - pool.tokens.indexOf(makerToken), - takerFillAmounts, + source, ), ); case ERC20BridgeSource.LiquidityProvider: @@ -1134,16 +1021,21 @@ export class SamplerOperations { this.getLiquidityProviderSellQuotes(pool, makerToken, takerToken, takerFillAmounts), ); case ERC20BridgeSource.MStable: - return this.getMStableSellQuotes(makerToken, takerToken, takerFillAmounts); + return isValidAddress(MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getMStableSellQuotes( + MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + takerFillAmounts, + ) + : []; case ERC20BridgeSource.Mooniswap: return [ - ...[ - MAINNET_MOONISWAP_REGISTRY, - MAINNET_MOONISWAP_V2_REGISTRY, - MAINNET_MOONISWAP_V2_1_REGISTRY, - ].map(registry => - this.getMooniswapSellQuotes(registry, makerToken, takerToken, takerFillAmounts), - ), + ...MOONISWAP_REGISTRIES_BY_CHAIN_ID[this.chainId] + .filter(r => isValidAddress(r)) + .map(registry => + this.getMooniswapSellQuotes(registry, makerToken, takerToken, takerFillAmounts), + ), ]; case ERC20BridgeSource.Balancer: return this.balancerPoolsCache @@ -1170,63 +1062,69 @@ export class SamplerOperations { ), ); case ERC20BridgeSource.Shell: - return getShellsForPair(takerToken, makerToken).map(pool => + return getShellsForPair(this.chainId, takerToken, makerToken).map(pool => this.getShellSellQuotes(pool, makerToken, takerToken, takerFillAmounts), ); case ERC20BridgeSource.Dodo: - return this.getDODOSellQuotes(makerToken, takerToken, takerFillAmounts); + if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { + return []; + } + return this.getDODOSellQuotes( + DODO_CONFIG_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + takerFillAmounts, + ); case ERC20BridgeSource.DodoV2: - return [ - ...getDodoV2Offsets().map(offset => - this.getDODOV2SellQuotes( - MAINNET_DODOV2_PRIVATE_POOL_FACTORY, - offset, - makerToken, - takerToken, - takerFillAmounts, + return _.flatten( + DODOV2_FACTORIES_BY_CHAIN_ID[this.chainId] + .filter(factory => isValidAddress(factory)) + .map(factory => + getDodoV2Offsets().map(offset => + this.getDODOV2SellQuotes( + factory, + offset, + makerToken, + takerToken, + takerFillAmounts, + ), + ), ), - ), - ...getDodoV2Offsets().map(offset => - this.getDODOV2SellQuotes( - MAINNET_DODOV2_VENDING_MACHINE_FACTORY, - offset, - makerToken, - takerToken, - takerFillAmounts, - ), - ), - ]; + ); case ERC20BridgeSource.Bancor: - return this.getBancorSellQuotes(makerToken, takerToken, takerFillAmounts); + if (!isValidAddress(BANCOR_REGISTRY_BY_CHAIN_ID[this.chainId])) { + return []; + } + return this.getBancorSellQuotes( + BANCOR_REGISTRY_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + takerFillAmounts, + ); case ERC20BridgeSource.Linkswap: - const linkOps = [ + if (!isValidAddress(LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId])) { + return []; + } + return [ + [takerToken, makerToken], + ...getIntermediateTokens(makerToken, takerToken, { + default: [TOKENS.LINK, TOKENS.WETH], + }).map(t => [takerToken, t, makerToken]), + ].map(path => this.getUniswapV2SellQuotes( - MAINNET_LINKSWAP_ROUTER, - [takerToken, makerToken], + LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId], + path, takerFillAmounts, ERC20BridgeSource.Linkswap, ), - ]; - // LINK is the base asset in many of the pools on Linkswap - getIntermediateTokens(makerToken, takerToken, { - default: [TOKENS.LINK, TOKENS.WETH], - }).forEach(t => { - linkOps.push( - this.getUniswapV2SellQuotes( - MAINNET_LINKSWAP_ROUTER, - [takerToken, t, makerToken], - takerFillAmounts, - ERC20BridgeSource.Linkswap, - ), - ); - }); - return linkOps; + ); default: throw new Error(`Unsupported sell sample source: ${source}`); } }, ), ); + return allOps; } private _getBuyQuoteOperations( @@ -1244,84 +1142,59 @@ export class SamplerOperations { (source): SourceQuoteOperation | SourceQuoteOperation[] => { switch (source) { case ERC20BridgeSource.Eth2Dai: - return this.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); + return isValidAddress(OASIS_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getEth2DaiBuyQuotes( + OASIS_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + makerFillAmounts, + ) + : []; case ERC20BridgeSource.Uniswap: - return this.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); + return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getUniswapBuyQuotes( + UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + makerFillAmounts, + ) + : []; case ERC20BridgeSource.UniswapV2: - const ops = [ - this.getUniswapV2BuyQuotes( - MAINNET_UNISWAP_V2_ROUTER, - [takerToken, makerToken], - makerFillAmounts, - ), - ]; - intermediateTokens.forEach(t => { - ops.push( - this.getUniswapV2BuyQuotes( - MAINNET_UNISWAP_V2_ROUTER, - [takerToken, t, makerToken], - makerFillAmounts, - ), - ); - }); - return ops; case ERC20BridgeSource.SushiSwap: - const sushiOps = [this.getSushiSwapBuyQuotes([takerToken, makerToken], makerFillAmounts)]; - intermediateTokens.forEach(t => { - sushiOps.push( - this.getSushiSwapBuyQuotes([takerToken, t, makerToken], makerFillAmounts), - ); - }); - return sushiOps; case ERC20BridgeSource.CryptoCom: - const cryptoComOps = [ - this.getUniswapV2BuyQuotes( - MAINNET_CRYPTO_COM_ROUTER, - [takerToken, makerToken], - makerFillAmounts, - ERC20BridgeSource.CryptoCom, - ), - ]; - intermediateTokens.forEach(t => { - cryptoComOps.push( - this.getUniswapV2BuyQuotes( - MAINNET_CRYPTO_COM_ROUTER, - [takerToken, t, makerToken], - makerFillAmounts, - ERC20BridgeSource.CryptoCom, - ), - ); - }); - return cryptoComOps; + case ERC20BridgeSource.PancakeSwap: + case ERC20BridgeSource.BakerySwap: + const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source); + if (!isValidAddress(uniLikeRouter)) { + return []; + } + return [ + [takerToken, makerToken], + ...intermediateTokens.map(t => [takerToken, t, makerToken]), + ].map(path => this.getUniswapV2BuyQuotes(uniLikeRouter, path, makerFillAmounts, source)); case ERC20BridgeSource.Kyber: return getKyberOffsets().map(offset => - this.getKyberBuyQuotes(offset, makerToken, takerToken, makerFillAmounts), + this.getKyberBuyQuotes( + KYBER_CONFIG_BY_CHAIN_ID[this.chainId], + offset, + makerToken, + takerToken, + makerFillAmounts, + ), ); case ERC20BridgeSource.Curve: - return getCurveInfosForPair(takerToken, makerToken).map(pool => + case ERC20BridgeSource.Swerve: + case ERC20BridgeSource.SnowSwap: + case ERC20BridgeSource.Nerve: + case ERC20BridgeSource.Belt: + case ERC20BridgeSource.Ellipsis: + return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool => this.getCurveBuyQuotes( pool, pool.tokens.indexOf(takerToken), pool.tokens.indexOf(makerToken), makerFillAmounts, - ), - ); - case ERC20BridgeSource.Swerve: - return getSwerveInfosForPair(takerToken, makerToken).map(pool => - this.getSwerveBuyQuotes( - pool, - pool.tokens.indexOf(takerToken), - pool.tokens.indexOf(makerToken), - makerFillAmounts, - ), - ); - case ERC20BridgeSource.SnowSwap: - return getSnowSwapInfosForPair(takerToken, makerToken).map(pool => - this.getSnowSwapBuyQuotes( - pool, - pool.tokens.indexOf(takerToken), - pool.tokens.indexOf(makerToken), - makerFillAmounts, + source, ), ); case ERC20BridgeSource.LiquidityProvider: @@ -1333,16 +1206,21 @@ export class SamplerOperations { this.getLiquidityProviderBuyQuotes(pool, makerToken, takerToken, makerFillAmounts), ); case ERC20BridgeSource.MStable: - return this.getMStableBuyQuotes(makerToken, takerToken, makerFillAmounts); + return isValidAddress(MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId]) + ? this.getMStableBuyQuotes( + MSTABLE_ROUTER_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + makerFillAmounts, + ) + : []; case ERC20BridgeSource.Mooniswap: return [ - ...[ - MAINNET_MOONISWAP_REGISTRY, - MAINNET_MOONISWAP_V2_REGISTRY, - MAINNET_MOONISWAP_V2_1_REGISTRY, - ].map(registry => - this.getMooniswapBuyQuotes(registry, makerToken, takerToken, makerFillAmounts), - ), + ...MOONISWAP_REGISTRIES_BY_CHAIN_ID[this.chainId] + .filter(r => isValidAddress(r)) + .map(registry => + this.getMooniswapBuyQuotes(registry, makerToken, takerToken, makerFillAmounts), + ), ]; case ERC20BridgeSource.Balancer: return this.balancerPoolsCache @@ -1369,57 +1247,57 @@ export class SamplerOperations { ), ); case ERC20BridgeSource.Shell: - return getShellsForPair(takerToken, makerToken).map(pool => + return getShellsForPair(this.chainId, takerToken, makerToken).map(pool => this.getShellBuyQuotes(pool, makerToken, takerToken, makerFillAmounts), ); case ERC20BridgeSource.Dodo: - return this.getDODOBuyQuotes(makerToken, takerToken, makerFillAmounts); + if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { + return []; + } + return this.getDODOBuyQuotes( + DODO_CONFIG_BY_CHAIN_ID[this.chainId], + makerToken, + takerToken, + makerFillAmounts, + ); case ERC20BridgeSource.DodoV2: - return [ - ...getDodoV2Offsets().map(offset => - this.getDODOV2BuyQuotes( - MAINNET_DODOV2_PRIVATE_POOL_FACTORY, - offset, - makerToken, - takerToken, - makerFillAmounts, + return _.flatten( + DODOV2_FACTORIES_BY_CHAIN_ID[this.chainId] + .filter(factory => isValidAddress(factory)) + .map(factory => + getDodoV2Offsets().map(offset => + this.getDODOV2BuyQuotes( + factory, + offset, + makerToken, + takerToken, + makerFillAmounts, + ), + ), ), - ), - ...getDodoV2Offsets().map(offset => - this.getDODOV2BuyQuotes( - MAINNET_DODOV2_VENDING_MACHINE_FACTORY, - offset, - makerToken, - takerToken, - makerFillAmounts, - ), - ), - ]; + ); case ERC20BridgeSource.Bancor: - return this.getBancorBuyQuotes(makerToken, takerToken, makerFillAmounts); + // Unimplemented + // return this.getBancorBuyQuotes(makerToken, takerToken, makerFillAmounts); + return []; case ERC20BridgeSource.Linkswap: - const linkOps = [ + if (!isValidAddress(LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId])) { + return []; + } + return [ + [takerToken, makerToken], + // LINK is the base asset in many of the pools on Linkswap + ...getIntermediateTokens(makerToken, takerToken, { + default: [TOKENS.LINK, TOKENS.WETH], + }).map(t => [takerToken, t, makerToken]), + ].map(path => this.getUniswapV2BuyQuotes( - MAINNET_LINKSWAP_ROUTER, - [takerToken, makerToken], + LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId], + path, makerFillAmounts, ERC20BridgeSource.Linkswap, ), - ]; - // LINK is the base asset in many of the pools on Linkswap - getIntermediateTokens(makerToken, takerToken, { - default: [TOKENS.LINK, TOKENS.WETH], - }).forEach(t => { - linkOps.push( - this.getUniswapV2BuyQuotes( - MAINNET_LINKSWAP_ROUTER, - [takerToken, t, makerToken], - makerFillAmounts, - ERC20BridgeSource.Linkswap, - ), - ); - }); - return linkOps; + ); default: throw new Error(`Unsupported buy sample source: ${source}`); } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/shell_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/shell_utils.ts deleted file mode 100644 index f1dfbf1635..0000000000 --- a/packages/asset-swapper/src/utils/market_operation_utils/shell_utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MAINNET_SHELL_POOLS } from './constants'; - -// tslint:disable completed-docs -export function getShellsForPair(takerToken: string, makerToken: string): string[] { - return Object.values(MAINNET_SHELL_POOLS) - .filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))) - .map(i => i.poolAddress); -} 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 d91702482e..059a0ef029 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -58,6 +58,12 @@ export enum ERC20BridgeSource { DodoV2 = 'DODO_V2', CryptoCom = 'CryptoCom', Linkswap = 'Linkswap', + // Other + PancakeSwap = 'PancakeSwap', + BakerySwap = 'BakerySwap', + Nerve = 'Nerve', + Belt = 'Belt', + Ellipsis = 'Ellipsis', } // tslint:disable: enum-naming @@ -72,6 +78,9 @@ export enum CurveFunctionSelectors { get_dx_underlying = '0x0e71d1b9', get_dy = '0x5e0d443f', get_dx = '0x67df02ca', + // Nerve BSC + swap = '0x91695586', + calculateSwap = '0xa95b089f', } // tslint:enable: enum-naming @@ -87,9 +96,6 @@ export interface CurveInfo { metaToken: string | undefined; } -export interface SwerveInfo extends CurveInfo {} -export interface SnowSwapInfo extends CurveInfo {} - // Internal `fillData` field for `Fill` objects. export interface FillData {} @@ -111,18 +117,6 @@ export interface CurveFillData extends FillData { pool: CurveInfo; } -export interface SwerveFillData extends FillData { - fromTokenIdx: number; - toTokenIdx: number; - pool: SwerveInfo; -} - -export interface SnowSwapFillData extends FillData { - fromTokenIdx: number; - toTokenIdx: number; - pool: SnowSwapInfo; -} - export interface BalancerFillData extends FillData { poolAddress: string; } @@ -132,8 +126,6 @@ export interface UniswapV2FillData extends FillData { router: string; } -export interface SushiSwapFillData extends UniswapV2FillData {} - export interface ShellFillData extends FillData { poolAddress: string; } @@ -151,6 +143,7 @@ export interface BancorFillData extends FillData { export interface KyberFillData extends FillData { hint: string; reserveId: string; + networkProxy: string; } export interface MooniswapFillData extends FillData { @@ -160,7 +153,13 @@ export interface MooniswapFillData extends FillData { export interface DODOFillData extends FillData { poolAddress: string; isSellBase: boolean; + helperAddress: string; } + +export interface GenericRouterFillData extends FillData { + router: string; +} + export interface MultiHopFillData extends FillData { firstHopSource: SourceQuoteOperation; secondHopSource: SourceQuoteOperation; @@ -440,3 +439,9 @@ export interface GenerateOptimizedOrdersOpts { export interface ComparisonPrice { wholeOrder: BigNumber | undefined; } + +export interface KyberSamplerOpts { + networkProxy: string; + hintHandler: string; + weth: string; +} diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index e55da43b91..f51646085c 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -10,7 +10,6 @@ import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json'; import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json'; import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json'; -import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json'; import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json'; import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json'; import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json'; @@ -36,7 +35,6 @@ import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSamp import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json'; import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json'; import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json'; -import * as SushiSwapSampler from '../test/generated-artifacts/SushiSwapSampler.json'; import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json'; import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json'; import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json'; @@ -51,7 +49,6 @@ export const artifacts = { CurveSampler: CurveSampler as ContractArtifact, DODOSampler: DODOSampler as ContractArtifact, DODOV2Sampler: DODOV2Sampler as ContractArtifact, - DeploymentConstants: DeploymentConstants as ContractArtifact, ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, Eth2DaiSampler: Eth2DaiSampler as ContractArtifact, FakeTaker: FakeTaker as ContractArtifact, @@ -63,7 +60,6 @@ export const artifacts = { NativeOrderSampler: NativeOrderSampler as ContractArtifact, SamplerUtils: SamplerUtils as ContractArtifact, ShellSampler: ShellSampler as ContractArtifact, - SushiSwapSampler: SushiSwapSampler as ContractArtifact, TwoHopSampler: TwoHopSampler as ContractArtifact, UniswapSampler: UniswapSampler as ContractArtifact, UniswapV2Sampler: UniswapV2Sampler as ContractArtifact, diff --git a/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts b/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts index 61c2dcbf28..fcee58267e 100644 --- a/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts +++ b/packages/asset-swapper/test/contracts/bridge_sampler_mainnet_test.ts @@ -1,7 +1,9 @@ +import { ChainId } from '@0x/contract-addresses'; import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils'; import { RPCSubprovider } from '@0x/subproviders'; -import { BigNumber, providerUtils } from '@0x/utils'; +import { BigNumber, NULL_BYTES, providerUtils } from '@0x/utils'; +import { KYBER_CONFIG_BY_CHAIN_ID, TOKENS } from '../../src/utils/market_operation_utils/constants'; import { artifacts } from '../artifacts'; import { ERC20BridgeSamplerContract } from '../wrappers'; @@ -78,28 +80,33 @@ blockchainTests.skip('Mainnet Sampler Tests', env => { }); }); describe('Kyber', () => { - const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'; - const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; + const WETH = TOKENS.WETH; + const DAI = TOKENS.DAI; + const USDC = TOKENS.USDC; const RESERVE_OFFSET = new BigNumber(0); + const KYBER_OPTS = { + ...KYBER_CONFIG_BY_CHAIN_ID[ChainId.Mainnet], + reserveOffset: RESERVE_OFFSET, + hint: NULL_BYTES, + }; describe('sampleSellsFromKyberNetwork()', () => { it('samples sells from Kyber DAI->WETH', async () => { const [, samples] = await testContract - .sampleSellsFromKyberNetwork(RESERVE_OFFSET, DAI, WETH, [toBaseUnitAmount(1)]) + .sampleSellsFromKyberNetwork(KYBER_OPTS, DAI, WETH, [toBaseUnitAmount(1)]) .callAsync({ overrides }); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); }); it('samples sells from Kyber WETH->DAI', async () => { const [, samples] = await testContract - .sampleSellsFromKyberNetwork(RESERVE_OFFSET, WETH, DAI, [toBaseUnitAmount(1)]) + .sampleSellsFromKyberNetwork(KYBER_OPTS, WETH, DAI, [toBaseUnitAmount(1)]) .callAsync({ overrides }); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); }); it('samples sells from Kyber DAI->USDC', async () => { const [, samples] = await testContract - .sampleSellsFromKyberNetwork(RESERVE_OFFSET, DAI, USDC, [toBaseUnitAmount(1)]) + .sampleSellsFromKyberNetwork(KYBER_OPTS, DAI, USDC, [toBaseUnitAmount(1)]) .callAsync({ overrides }); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); @@ -111,7 +118,7 @@ blockchainTests.skip('Mainnet Sampler Tests', env => { // From ETH to DAI // I want to buy 1 DAI const [, samples] = await testContract - .sampleBuysFromKyberNetwork(RESERVE_OFFSET, WETH, DAI, [toBaseUnitAmount(1)]) + .sampleBuysFromKyberNetwork(KYBER_OPTS, WETH, DAI, [toBaseUnitAmount(1)]) .callAsync({ overrides }); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); @@ -121,7 +128,7 @@ blockchainTests.skip('Mainnet Sampler Tests', env => { // From USDC to DAI // I want to buy 1 WETH const [, samples] = await testContract - .sampleBuysFromKyberNetwork(RESERVE_OFFSET, DAI, WETH, [toBaseUnitAmount(1)]) + .sampleBuysFromKyberNetwork(KYBER_OPTS, DAI, WETH, [toBaseUnitAmount(1)]) .callAsync({ overrides }); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); diff --git a/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts b/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts index fde261f51e..3754cfe28a 100644 --- a/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts +++ b/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts @@ -31,12 +31,15 @@ blockchainTests('erc20-bridge-sampler', env => { const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7'; const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab'; const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1'; - let UNISWAP_V2_ROUTER = ''; const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR'; const MAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress(); const INTERMEDIATE_TOKEN = randomAddress(); const KYBER_RESERVE_OFFSET = new BigNumber(0); + let KYBER_ADDRESS = ''; + let ETH2DAI_ADDRESS = ''; + let UNISWAP_ADDRESS = ''; + let UNISWAP_V2_ROUTER = ''; before(async () => { testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync( @@ -46,6 +49,9 @@ blockchainTests('erc20-bridge-sampler', env => { {}, ); UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync(); + KYBER_ADDRESS = await testContract.kyber().callAsync(); + ETH2DAI_ADDRESS = await testContract.eth2Dai().callAsync(); + UNISWAP_ADDRESS = await testContract.uniswap().callAsync(); }); function getPackedHash(...args: string[]): string { @@ -331,20 +337,30 @@ blockchainTests('erc20-bridge-sampler', env => { }); blockchainTests.resets('sampleSellsFromKyberNetwork()', () => { + let kyberOpts = { + hintHandler: NULL_ADDRESS, + networkProxy: NULL_ADDRESS, + weth: WETH_ADDRESS, + reserveOffset: KYBER_RESERVE_OFFSET, + hint: NULL_BYTES, + }; before(async () => { await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); + kyberOpts = { + ...kyberOpts, + hintHandler: KYBER_ADDRESS, + networkProxy: KYBER_ADDRESS, + }; }); it('throws if tokens are the same', async () => { - const tx = testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, MAKER_TOKEN, MAKER_TOKEN, []) - .callAsync(); + const tx = testContract.sampleSellsFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, []) + .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, []) .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -354,7 +370,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -363,7 +379,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -372,7 +388,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -382,7 +398,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -391,7 +407,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -401,28 +417,38 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleSellsFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); }); blockchainTests.resets('sampleBuysFromKyberNetwork()', () => { + let kyberOpts = { + hintHandler: NULL_ADDRESS, + networkProxy: NULL_ADDRESS, + weth: WETH_ADDRESS, + reserveOffset: KYBER_RESERVE_OFFSET, + hint: NULL_BYTES, + }; const ACCEPTABLE_SLIPPAGE = 0.0005; before(async () => { await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); + kyberOpts = { + ...kyberOpts, + hintHandler: KYBER_ADDRESS, + networkProxy: KYBER_ADDRESS, + }; }); it('throws if tokens are the same', async () => { - const tx = testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, MAKER_TOKEN, MAKER_TOKEN, []) - .callAsync(); + const tx = testContract.sampleBuysFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, []) + .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, []) .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -431,7 +457,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -441,7 +467,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -450,7 +476,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -460,7 +486,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -469,7 +495,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -479,7 +505,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const [, , quotes] = await testContract - .sampleBuysFromKyberNetwork(KYBER_RESERVE_OFFSET, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -491,12 +517,14 @@ blockchainTests('erc20-bridge-sampler', env => { }); it('throws if tokens are the same', async () => { - const tx = testContract.sampleSellsFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const tx = testContract.sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { - const quotes = await testContract.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const quotes = await testContract + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) + .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -504,7 +532,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -514,7 +542,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -523,7 +551,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -533,7 +561,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -542,7 +570,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -552,7 +580,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -564,12 +592,14 @@ blockchainTests('erc20-bridge-sampler', env => { }); it('throws if tokens are the same', async () => { - const tx = testContract.sampleBuysFromEth2Dai(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const tx = testContract.sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { - const quotes = await testContract.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const quotes = await testContract + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) + .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -577,7 +607,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -587,7 +617,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -596,7 +626,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -606,7 +636,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -615,7 +645,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts); const quotes = await testContract - .sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -625,24 +655,27 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); }); blockchainTests.resets('sampleSellsFromUniswap()', () => { + const UNISWAP_ETH_ADDRESS = NULL_ADDRESS; before(async () => { await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); }); it('throws if tokens are the same', async () => { - const tx = testContract.sampleSellsFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const tx = testContract.sampleSellsFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { - const quotes = await testContract.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const quotes = await testContract + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) + .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -650,7 +683,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -660,7 +693,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -669,7 +702,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -679,7 +712,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -688,7 +721,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -698,7 +731,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -708,7 +741,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const quotes = await testContract - .sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -718,24 +751,27 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(nonExistantToken); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const quotes = await testContract - .sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts) + .sampleSellsFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); }); blockchainTests.resets('sampleBuysFromUniswap()', () => { + const UNISWAP_ETH_ADDRESS = NULL_ADDRESS; before(async () => { await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); }); it('throws if tokens are the same', async () => { - const tx = testContract.sampleBuysFromUniswap(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const tx = testContract.sampleBuysFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { - const quotes = await testContract.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const quotes = await testContract + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, []) + .callAsync(); expect(quotes).to.deep.eq([]); }); @@ -743,7 +779,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -753,7 +789,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -762,7 +798,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -772,7 +808,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -781,7 +817,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts); const quotes = await testContract - .sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -791,7 +827,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -801,7 +837,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(nonExistantToken); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const quotes = await testContract - .sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -811,7 +847,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(MAKER_TOKEN); const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); const quotes = await testContract - .sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -1060,10 +1096,10 @@ blockchainTests('erc20-bridge-sampler', env => { .getABIEncodedTransactionData(); const eth2DaiFirstHop = testContract - .sampleSellsFromEth2Dai(TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT]) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT]) .getABIEncodedTransactionData(); const eth2DaiSecondHop = testContract - .sampleSellsFromEth2Dai(INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT]) + .sampleSellsFromEth2Dai(ETH2DAI_ADDRESS, INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT]) .getABIEncodedTransactionData(); const firstHopQuotes = [ @@ -1111,10 +1147,10 @@ blockchainTests('erc20-bridge-sampler', env => { .getABIEncodedTransactionData(); const eth2DaiFirstHop = testContract - .sampleBuysFromEth2Dai(TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT]) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, TAKER_TOKEN, INTERMEDIATE_TOKEN, [constants.ZERO_AMOUNT]) .getABIEncodedTransactionData(); const eth2DaiSecondHop = testContract - .sampleBuysFromEth2Dai(INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT]) + .sampleBuysFromEth2Dai(ETH2DAI_ADDRESS, INTERMEDIATE_TOKEN, MAKER_TOKEN, [constants.ZERO_AMOUNT]) .getABIEncodedTransactionData(); const secondHopQuotes = [ diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts index 9d8c52d40f..eb40a190b5 100644 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ b/packages/asset-swapper/test/dex_sampler_test.ts @@ -1,4 +1,4 @@ -import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; +import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; import { constants, expect, @@ -26,6 +26,7 @@ const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000 describe('DexSampler tests', () => { const MAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress(); + const chainId = ChainId.Mainnet; const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken; const exchangeProxyAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchangeProxy; @@ -105,6 +106,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -129,6 +131,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -157,6 +160,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -167,6 +171,7 @@ describe('DexSampler tests', () => { ); const [fillableAmounts] = await dexOrderSampler.executeAsync( dexOrderSampler.getKyberSellQuotes( + { hintHandler: randomAddress(), networkProxy: randomAddress(), weth: randomAddress() }, new BigNumber(0), expectedMakerToken, expectedTakerToken, @@ -190,6 +195,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -234,6 +240,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -270,7 +277,7 @@ describe('DexSampler tests', () => { const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const sampler = new MockSamplerContract({ - sampleSellsFromEth2Dai: (takerToken, makerToken, fillAmounts) => { + sampleSellsFromEth2Dai: (_router, takerToken, makerToken, fillAmounts) => { expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); @@ -278,6 +285,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -287,7 +295,12 @@ describe('DexSampler tests', () => { async () => undefined, ); const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getEth2DaiSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts), + dexOrderSampler.getEth2DaiSellQuotes( + randomAddress(), + expectedMakerToken, + expectedTakerToken, + expectedTakerFillAmounts, + ), ); expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); }); @@ -298,7 +311,7 @@ describe('DexSampler tests', () => { const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const sampler = new MockSamplerContract({ - sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => { + sampleSellsFromUniswap: (_router, takerToken, makerToken, fillAmounts) => { expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); @@ -306,6 +319,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -315,7 +329,12 @@ describe('DexSampler tests', () => { async () => undefined, ); const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getUniswapSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts), + dexOrderSampler.getUniswapSellQuotes( + randomAddress(), + expectedMakerToken, + expectedTakerToken, + expectedTakerFillAmounts, + ), ); expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); }); @@ -333,6 +352,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -357,7 +377,7 @@ describe('DexSampler tests', () => { const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const sampler = new MockSamplerContract({ - sampleBuysFromEth2Dai: (takerToken, makerToken, fillAmounts) => { + sampleBuysFromEth2Dai: (_router, takerToken, makerToken, fillAmounts) => { expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); @@ -365,6 +385,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -374,7 +395,12 @@ describe('DexSampler tests', () => { async () => undefined, ); const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getEth2DaiBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts), + dexOrderSampler.getEth2DaiBuyQuotes( + randomAddress(), + expectedMakerToken, + expectedTakerToken, + expectedMakerFillAmounts, + ), ); expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts); }); @@ -385,7 +411,7 @@ describe('DexSampler tests', () => { const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); const sampler = new MockSamplerContract({ - sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => { + sampleBuysFromUniswap: (_router, takerToken, makerToken, fillAmounts) => { expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); @@ -393,6 +419,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -402,7 +429,12 @@ describe('DexSampler tests', () => { async () => undefined, ); const [fillableAmounts] = await dexOrderSampler.executeAsync( - dexOrderSampler.getUniswapBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts), + dexOrderSampler.getUniswapBuyQuotes( + randomAddress(), + expectedMakerToken, + expectedTakerToken, + expectedMakerFillAmounts, + ), ); expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts); }); @@ -423,21 +455,25 @@ describe('DexSampler tests', () => { }; const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); let uniswapRouter: string; + let uniswapV2Router: string; + let eth2DaiRouter: string; const sampler = new MockSamplerContract({ - sampleSellsFromUniswap: (takerToken, makerToken, fillAmounts) => { + sampleSellsFromUniswap: (router, takerToken, makerToken, fillAmounts) => { + uniswapRouter = router; expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()); }, - sampleSellsFromEth2Dai: (takerToken, makerToken, fillAmounts) => { + sampleSellsFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => { + eth2DaiRouter = router; expect(takerToken).to.eq(expectedTakerToken); expect(makerToken).to.eq(expectedMakerToken); expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); }, sampleSellsFromUniswapV2: (router, path, fillAmounts) => { - uniswapRouter = router; + uniswapV2Router = router; if (path.length === 2) { expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); } else if (path.length === 3) { @@ -450,6 +486,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -471,10 +508,22 @@ describe('DexSampler tests', () => { source: s, input: a, output: a.times(ratesBySource[s]).integerValue(), - fillData: - s === ERC20BridgeSource.UniswapV2 - ? { router: uniswapRouter, tokenAddressPath: [expectedTakerToken, expectedMakerToken] } - : {}, + fillData: (() => { + if (s === ERC20BridgeSource.UniswapV2) { + return { + router: uniswapV2Router, + tokenAddressPath: [expectedTakerToken, expectedMakerToken], + }; + } + if (s === ERC20BridgeSource.Eth2Dai) { + return { router: eth2DaiRouter }; + } + // TODO jacob pass through + if (s === ERC20BridgeSource.Uniswap) { + return { router: uniswapRouter }; + } + return {}; + })(), })), ); const uniswapV2ETHQuotes = [ @@ -483,7 +532,7 @@ describe('DexSampler tests', () => { input: a, output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), fillData: { - router: uniswapRouter, + router: uniswapV2Router, tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], }, })), @@ -509,6 +558,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, new MockSamplerContract({}), undefined, balancerPoolsCache, @@ -535,21 +585,25 @@ describe('DexSampler tests', () => { }; const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); let uniswapRouter: string; + let uniswapV2Router: string; + let eth2DaiRouter: string; const sampler = new MockSamplerContract({ - sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => { + sampleBuysFromUniswap: (router, takerToken, makerToken, fillAmounts) => { + uniswapRouter = router; 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) => { + sampleBuysFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => { + eth2DaiRouter = router; 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: (router, path, fillAmounts) => { - uniswapRouter = router; + uniswapV2Router = router; if (path.length === 2) { expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); } else if (path.length === 3) { @@ -562,6 +616,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, @@ -578,10 +633,21 @@ describe('DexSampler tests', () => { source: s, input: a, output: a.times(ratesBySource[s]).integerValue(), - fillData: - s === ERC20BridgeSource.UniswapV2 - ? { router: uniswapRouter, tokenAddressPath: [expectedTakerToken, expectedMakerToken] } - : {}, + fillData: (() => { + if (s === ERC20BridgeSource.UniswapV2) { + return { + router: uniswapV2Router, + tokenAddressPath: [expectedTakerToken, expectedMakerToken], + }; + } + if (s === ERC20BridgeSource.Eth2Dai) { + return { router: eth2DaiRouter }; + } + if (s === ERC20BridgeSource.Uniswap) { + return { router: uniswapRouter }; + } + return {}; + })(), })), ); const uniswapV2ETHQuotes = [ @@ -590,7 +656,7 @@ describe('DexSampler tests', () => { input: a, output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(), fillData: { - router: uniswapRouter, + router: uniswapV2Router, tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken], }, })), @@ -612,6 +678,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, new MockSamplerContract({}), undefined, balancerPoolsCache, @@ -646,6 +713,7 @@ describe('DexSampler tests', () => { }, }); const dexOrderSampler = new DexOrderSampler( + chainId, sampler, undefined, undefined, diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 68c6da073f..94bd16bcec 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -19,9 +19,9 @@ import { NativeOrderWithFillableAmounts } from '../src/types'; import { MarketOperationUtils } from '../src/utils/market_operation_utils/'; import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils'; import { - BUY_SOURCE_FILTER, + BUY_SOURCE_FILTER_BY_CHAIN_ID, POSITIVE_INF, - SELL_SOURCE_FILTER, + SELL_SOURCE_FILTER_BY_CHAIN_ID, SOURCE_FLAGS, } from '../src/utils/market_operation_utils/constants'; import { CreamPoolsCache } from '../src/utils/market_operation_utils/cream_utils'; @@ -65,9 +65,11 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.LiquidityProvider, ERC20BridgeSource.CryptoCom, ERC20BridgeSource.Linkswap, + ERC20BridgeSource.PancakeSwap, + ERC20BridgeSource.BakerySwap, ]; -const BUY_SOURCES = BUY_SOURCE_FILTER.sources; -const SELL_SOURCES = SELL_SOURCE_FILTER.sources; +const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; +const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources; const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] }; const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign }; @@ -301,6 +303,8 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.DodoV2]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CryptoCom]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Linkswap]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.PancakeSwap]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.BakerySwap]: _.times(NUM_SAMPLES, () => 0), }; const DEFAULT_RATES: RatesBySource = { @@ -318,7 +322,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] }, [ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() }, [ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() }, - [ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x' }, + [ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x', networkAddress: randomAddress() }, [ERC20BridgeSource.Curve]: { pool: { poolAddress: randomAddress(), @@ -363,6 +367,8 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.DodoV2]: {}, [ERC20BridgeSource.CryptoCom]: { tokenAddressPath: [] }, [ERC20BridgeSource.Linkswap]: { tokenAddressPath: [] }, + [ERC20BridgeSource.Uniswap]: { router: randomAddress() }, + [ERC20BridgeSource.Eth2Dai]: { router: randomAddress() }, }; const DEFAULT_OPS = { @@ -441,6 +447,7 @@ describe('MarketOperationUtils tests', () => { balancerPoolsCache: new BalancerPoolsCache(), creamPoolsCache: new CreamPoolsCache(), liquidityProviderRegistry: {}, + chainId: CHAIN_ID, } as any) as DexOrderSampler; function replaceSamplerOps(ops: Partial = {}): void { diff --git a/packages/asset-swapper/test/utils/mock_sampler_contract.ts b/packages/asset-swapper/test/utils/mock_sampler_contract.ts index a5c03238d5..0c263540d4 100644 --- a/packages/asset-swapper/test/utils/mock_sampler_contract.ts +++ b/packages/asset-swapper/test/utils/mock_sampler_contract.ts @@ -1,9 +1,10 @@ import { ContractTxFunctionObj } from '@0x/base-contract'; import { constants } from '@0x/contracts-test-utils'; import { LimitOrderFields, Signature } from '@0x/protocol-utils'; -import { BigNumber, hexUtils } from '@0x/utils'; +import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils'; import { SamplerCallResult } from '../../src/types'; +import { KyberSamplerOpts } from '../../src/utils/market_operation_utils/types'; import { ERC20BridgeSamplerContract } from '../../src/wrappers'; export type GetOrderFillableAssetAmountResult = BigNumber[]; @@ -14,18 +15,32 @@ export type GetOrderFillableAssetAmountHandler = ( ) => GetOrderFillableAssetAmountResult; export type SampleResults = BigNumber[]; -export type SampleSellsHandler = ( +export type SampleSellsUniswapHandler = ( + router: string, takerToken: string, makerToken: string, takerTokenAmounts: BigNumber[], ) => SampleResults; -export type SampleBuysHandler = ( +export type SampleBuysUniswapHandler = ( + router: string, + takerToken: string, + makerToken: string, + makerTokenAmounts: BigNumber[], +) => SampleResults; +export type SampleSellsEth2DaiHandler = ( + router: string, + takerToken: string, + makerToken: string, + takerTokenAmounts: BigNumber[], +) => SampleResults; +export type SampleBuysEth2DaiHandler = ( + router: string, takerToken: string, makerToken: string, makerTokenAmounts: BigNumber[], ) => SampleResults; export type SampleSellsKyberHandler = ( - reserveOffset: BigNumber, + opts: KyberSamplerOpts, takerToken: string, makerToken: string, takerTokenAmounts: BigNumber[], @@ -57,11 +72,11 @@ interface Handlers { getLimitOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler; sampleSellsFromKyberNetwork: SampleSellsKyberHandler; sampleSellsFromLiquidityProvider: SampleSellsLPHandler; - sampleSellsFromEth2Dai: SampleSellsHandler; - sampleSellsFromUniswap: SampleSellsHandler; + sampleSellsFromEth2Dai: SampleSellsEth2DaiHandler; + sampleSellsFromUniswap: SampleSellsUniswapHandler; sampleSellsFromUniswapV2: SampleUniswapV2Handler; - sampleBuysFromEth2Dai: SampleBuysHandler; - sampleBuysFromUniswap: SampleBuysHandler; + sampleBuysFromEth2Dai: SampleBuysEth2DaiHandler; + sampleBuysFromUniswap: SampleBuysUniswapHandler; sampleBuysFromUniswapV2: SampleUniswapV2Handler; sampleBuysFromLiquidityProvider: SampleSellsLPHandler; } @@ -111,7 +126,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { } public sampleSellsFromKyberNetwork( - reserveOffset: BigNumber, + opts: KyberSamplerOpts, takerToken: string, makerToken: string, takerAssetAmounts: BigNumber[], @@ -119,7 +134,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { return this._wrapCall( super.sampleSellsFromKyberNetwork, this._handlers.sampleSellsFromKyberNetwork, - reserveOffset, + { ...opts, reserveOffset: new BigNumber(1), hint: NULL_BYTES }, takerToken, makerToken, takerAssetAmounts, @@ -127,6 +142,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { } public sampleSellsFromEth2Dai( + router: string, takerToken: string, makerToken: string, takerAssetAmounts: BigNumber[], @@ -134,6 +150,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { return this._wrapCall( super.sampleSellsFromEth2Dai, this._handlers.sampleSellsFromEth2Dai, + router, takerToken, makerToken, takerAssetAmounts, @@ -141,6 +158,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { } public sampleSellsFromUniswap( + router: string, takerToken: string, makerToken: string, takerAssetAmounts: BigNumber[], @@ -148,6 +166,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { return this._wrapCall( super.sampleSellsFromUniswap, this._handlers.sampleSellsFromUniswap, + router, takerToken, makerToken, takerAssetAmounts, @@ -185,6 +204,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { } public sampleBuysFromEth2Dai( + router: string, takerToken: string, makerToken: string, makerAssetAmounts: BigNumber[], @@ -192,6 +212,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { return this._wrapCall( super.sampleBuysFromEth2Dai, this._handlers.sampleBuysFromEth2Dai, + router, takerToken, makerToken, makerAssetAmounts, @@ -199,6 +220,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { } public sampleBuysFromUniswap( + router: string, takerToken: string, makerToken: string, makerAssetAmounts: BigNumber[], @@ -206,6 +228,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract { return this._wrapCall( super.sampleBuysFromUniswap, this._handlers.sampleBuysFromUniswap, + router, takerToken, makerToken, makerAssetAmounts, diff --git a/packages/asset-swapper/test/utils/test_helpers.ts b/packages/asset-swapper/test/utils/test_helpers.ts index 5f4433938d..250b9b36a9 100644 --- a/packages/asset-swapper/test/utils/test_helpers.ts +++ b/packages/asset-swapper/test/utils/test_helpers.ts @@ -43,7 +43,7 @@ export const testHelpers = { afterResponseCallback: () => Promise, axiosClient: AxiosInstance = axios, ): Promise => { - const mockedAxios = new AxiosMockAdapter(axiosClient, { onNoMatch: 'throwException' }); + const mockedAxios = new AxiosMockAdapter(axiosClient, { onNoMatch: 'throwException' } as any); try { // Mock out Standard RFQ-T/M responses for (const mockedResponse of standardMockedResponses) { diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index 0d2b01bc8d..d13d62bca6 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -10,7 +10,6 @@ export * from '../test/generated-wrappers/bancor_sampler'; export * from '../test/generated-wrappers/curve_sampler'; export * from '../test/generated-wrappers/d_o_d_o_sampler'; export * from '../test/generated-wrappers/d_o_d_o_v2_sampler'; -export * from '../test/generated-wrappers/deployment_constants'; export * from '../test/generated-wrappers/dummy_liquidity_provider'; export * from '../test/generated-wrappers/erc20_bridge_sampler'; export * from '../test/generated-wrappers/eth2_dai_sampler'; @@ -34,7 +33,6 @@ export * from '../test/generated-wrappers/multi_bridge_sampler'; export * from '../test/generated-wrappers/native_order_sampler'; export * from '../test/generated-wrappers/sampler_utils'; export * from '../test/generated-wrappers/shell_sampler'; -export * from '../test/generated-wrappers/sushi_swap_sampler'; export * from '../test/generated-wrappers/test_erc20_bridge_sampler'; export * from '../test/generated-wrappers/test_native_order_sampler'; export * from '../test/generated-wrappers/two_hop_sampler'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json index 7d9358b535..544e02da22 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -13,7 +13,6 @@ "test/generated-artifacts/CurveSampler.json", "test/generated-artifacts/DODOSampler.json", "test/generated-artifacts/DODOV2Sampler.json", - "test/generated-artifacts/DeploymentConstants.json", "test/generated-artifacts/DummyLiquidityProvider.json", "test/generated-artifacts/ERC20BridgeSampler.json", "test/generated-artifacts/Eth2DaiSampler.json", @@ -37,7 +36,6 @@ "test/generated-artifacts/NativeOrderSampler.json", "test/generated-artifacts/SamplerUtils.json", "test/generated-artifacts/ShellSampler.json", - "test/generated-artifacts/SushiSwapSampler.json", "test/generated-artifacts/TestERC20BridgeSampler.json", "test/generated-artifacts/TestNativeOrderSampler.json", "test/generated-artifacts/TwoHopSampler.json", diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 7a5a791ba0..f1b849ae01 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,4 +1,21 @@ [ + { + "version": "6.0.0-bsc.0", + "changes": [ + { + "note": "Add BSC chain addresses", + "pr": 164 + }, + { + "note": "Remove exchangeProxyAllowanceTarget", + "pr": 164 + }, + { + "note": "Redeployed FQT on BSC", + "pr": 181 + } + ] + }, { "version": "5.11.0", "changes": [ diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index b78f7860d3..e4fed7822b 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -29,7 +29,6 @@ "dexForwarderBridge": "0xc47b7094f378e54347e281aab170e8cca69d880a", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", - "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyTransformerDeployer": "0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyLiquidityProviderSandbox": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a", @@ -71,7 +70,6 @@ "dexForwarderBridge": "0x3261ea1411a1a840aed708896f779e1b837c917e", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", - "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyLiquidityProviderSandbox": "0x53a3a41047ae6f6a593df847e3bb287ecd3ac825", @@ -113,7 +111,6 @@ "dexForwarderBridge": "0x0000000000000000000000000000000000000000", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", - "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyLiquidityProviderSandbox": "0x2e2090562076197f94f8d1beac0963b6d4c118b6", @@ -155,7 +152,6 @@ "dexForwarderBridge": "0x985d1a95c6a86a3bf85c4d425af984abceaf01de", "exchangeProxyGovernor": "0x618f9c67ce7bf1a50afa1e7e0238422601b0ff6e", "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", - "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyTransformerDeployer": "0x1b62de2dbb5e7aa519e9c442721ecef75702807f", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyLiquidityProviderSandbox": "0x4022e3982f326455f0905de3dbc4449999baf2dc", @@ -167,6 +163,47 @@ "positiveSlippageFeeTransformer": "0x0000000000000000000000000000000000000000" } }, + "56": { + "erc20Proxy": "0x0000000000000000000000000000000000000000", + "erc721Proxy": "0x0000000000000000000000000000000000000000", + "zrxToken": "0x0000000000000000000000000000000000000000", + "etherToken": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", + "exchangeV2": "0x0000000000000000000000000000000000000000", + "exchange": "0x0000000000000000000000000000000000000000", + "assetProxyOwner": "0x0000000000000000000000000000000000000000", + "zeroExGovernor": "0x0000000000000000000000000000000000000000", + "forwarder": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x0000000000000000000000000000000000000000", + "coordinator": "0x0000000000000000000000000000000000000000", + "multiAssetProxy": "0x0000000000000000000000000000000000000000", + "staticCallProxy": "0x0000000000000000000000000000000000000000", + "erc1155Proxy": "0x0000000000000000000000000000000000000000", + "devUtils": "0x0000000000000000000000000000000000000000", + "zrxVault": "0x0000000000000000000000000000000000000000", + "staking": "0x0000000000000000000000000000000000000000", + "stakingProxy": "0x0000000000000000000000000000000000000000", + "erc20BridgeProxy": "0x0000000000000000000000000000000000000000", + "erc20BridgeSampler": "0x0000000000000000000000000000000000000000", + "chaiBridge": "0x0000000000000000000000000000000000000000", + "dydxBridge": "0x0000000000000000000000000000000000000000", + "godsUnchainedValidator": "0x0000000000000000000000000000000000000000", + "broker": "0x0000000000000000000000000000000000000000", + "chainlinkStopLimit": "0x0000000000000000000000000000000000000000", + "maximumGasPrice": "0x0000000000000000000000000000000000000000", + "dexForwarderBridge": "0x0000000000000000000000000000000000000000", + "exchangeProxyGovernor": "0xccc9769c1a58766e79423a34b2cc5052d65c1983", + "exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff", + "exchangeProxyTransformerDeployer": "0x8224aa8fe5c9f07d5a59c735386ff6cc6aaeb568", + "exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498", + "exchangeProxyLiquidityProviderSandbox": "0xde7b2747624a647600fdb349184d0448ab954929", + "transformers": { + "wethTransformer": "0xac3d95668c092e895cd83a9cbafe9c7d9906471f", + "payTakerTransformer": "0x4f5e8ca2cadecd4a467ae441e4b03de4278a4574", + "affiliateFeeTransformer": "0x1be34ab9b2acb5c4ddd89454bdce637967e65230", + "fillQuoteTransformer": "0xfa8ca57cb24cd59e74ae1659a00104188e7e8a3e", + "positiveSlippageFeeTransformer": "0x7f5c79ad1788573b1145f4651a248523c54f5d1f" + } + }, "1337": { "erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48", "erc721Proxy": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401", @@ -197,16 +234,15 @@ "dexForwarderBridge": "0x0000000000000000000000000000000000000000", "exchangeProxyGovernor": "0x0000000000000000000000000000000000000000", "exchangeProxy": "0x5315e44798395d4a952530d131249fe00f554565", - "exchangeProxyAllowanceTarget": "0x8362c3ebd90041b30ec45908332e592721642637", "exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631", "exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa", "exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000", "transformers": { - "wethTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", - "payTakerTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a", - "affiliateFeeTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db", - "fillQuoteTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace", - "positiveSlippageFeeTransformer": "0xdd66c23e07b4d6925b6089b5fe6fc9e62941afe8" + "wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5", + "payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", + "affiliateFeeTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a", + "fillQuoteTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db", + "positiveSlippageFeeTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace" } } } diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index dc8f07e8ca..05dc13d8e4 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -30,7 +30,6 @@ export interface ContractAddresses { dexForwarderBridge: string; exchangeProxyGovernor: string; exchangeProxy: string; - exchangeProxyAllowanceTarget: string; exchangeProxyTransformerDeployer: string; exchangeProxyFlashWallet: string; exchangeProxyLiquidityProviderSandbox: string; @@ -49,6 +48,7 @@ export enum ChainId { Rinkeby = 4, Kovan = 42, Ganache = 1337, + BSC = 56, } /** diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index 2c028f164b..5898cad3d3 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "3.14.0", + "changes": [ + { + "note": "Update artifacts", + "pr": 164 + } + ] + }, { "version": "3.13.0", "changes": [ diff --git a/packages/contract-artifacts/artifacts/IZeroEx.json b/packages/contract-artifacts/artifacts/IZeroEx.json index 049da0e6bb..6d88dbfec8 100644 --- a/packages/contract-artifacts/artifacts/IZeroEx.json +++ b/packages/contract-artifacts/artifacts/IZeroEx.json @@ -284,18 +284,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { "internalType": "contract IERC20TokenV06", "name": "token", "type": "address" }, - { "internalType": "address", "name": "owner", "type": "address" }, - { "internalType": "address", "name": "to", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "_spendERC20Tokens", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -973,13 +961,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "getAllowanceTarget", - "outputs": [{ "internalType": "address", "name": "target", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -1302,16 +1283,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { "internalType": "contract IERC20TokenV06", "name": "token", "type": "address" }, - { "internalType": "address", "name": "owner", "type": "address" } - ], - "name": "getSpendableERC20BalanceOf", - "outputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "getTransformWallet", @@ -1406,6 +1377,18 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { "internalType": "contract IERC20TokenV06[]", "name": "tokens", "type": "address[]" }, + { "internalType": "uint256", "name": "sellAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }, + { "internalType": "enum IPancakeSwapFeature.ProtocolFork", "name": "fork", "type": "uint8" } + ], + "name": "sellToPancakeSwap", + "outputs": [{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { "internalType": "contract IERC20TokenV06[]", "name": "tokens", "type": "address[]" }, @@ -1499,15 +1482,6 @@ "takerTokenFilledAmount": "How much maker token was filled." } }, - "_spendERC20Tokens(address,address,address,uint256)": { - "details": "Transfers ERC20 tokens from `owner` to `to`. Only callable from within.", - "params": { - "amount": "The amount of `token` to transfer.", - "owner": "The owner of the tokens.", - "to": "The recipient of the tokens.", - "token": "The token to spend." - } - }, "_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[]))": { "details": "Internal version of `transformERC20()`. Only callable from within.", "params": { "args": "A `TransformERC20Args` struct." }, @@ -1679,10 +1653,6 @@ "takerTokenFilledAmount": "How much maker token was filled." } }, - "getAllowanceTarget()": { - "details": "Get the address of the allowance target.", - "returns": { "target": "The target of token allowances." } - }, "getLimitOrderHash((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256))": { "details": "Get the canonical hash of a limit order.", "params": { "order": "The limit order." }, @@ -1756,11 +1726,6 @@ "rollbackLength": "The number of items in the rollback history for the function." } }, - "getSpendableERC20BalanceOf(address,address)": { - "details": "Gets the maximum amount of an ERC20 token `token` that can be pulled from `owner`.", - "params": { "owner": "The owner of the tokens.", "token": "The token to spend." }, - "returns": { "amount": "The amount of tokens that can be pulled." } - }, "getTransformWallet()": { "details": "Return the current wallet instance that will serve as the execution context for transformations.", "returns": { "wallet": "The wallet instance." } @@ -1816,6 +1781,16 @@ }, "returns": { "boughtAmount": "The amount of `outputToken` bought." } }, + "sellToPancakeSwap(address[],uint256,uint256,uint8)": { + "details": "Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap.", + "params": { + "fork": "The protocol fork to use.", + "minBuyAmount": "Minimum amount of `tokens[-1]` to buy.", + "sellAmount": "of `tokens[0]` Amount to sell.", + "tokens": "Sell path." + }, + "returns": { "buyAmount": "Amount of `tokens[-1]` bought." } + }, "sellToUniswap(address[],uint256,uint256,bool)": { "details": "Efficiently sell directly to uniswap/sushiswap.", "params": { diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 96dd162e0b..8aa72f34d4 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "13.15.0", + "changes": [ + { + "note": "Regenerate wrappers", + "pr": 164 + } + ] + }, { "version": "13.14.0", "changes": [ diff --git a/packages/contract-wrappers/src/generated-wrappers/i_zero_ex.ts b/packages/contract-wrappers/src/generated-wrappers/i_zero_ex.ts index 3154fb760a..df8ed6c797 100644 --- a/packages/contract-wrappers/src/generated-wrappers/i_zero_ex.ts +++ b/packages/contract-wrappers/src/generated-wrappers/i_zero_ex.ts @@ -910,30 +910,6 @@ export class IZeroExContract extends BaseContract { stateMutability: 'nonpayable', type: 'function', }, - { - inputs: [ - { - name: 'token', - type: 'address', - }, - { - name: 'owner', - type: 'address', - }, - { - name: 'to', - type: 'address', - }, - { - name: 'amount', - type: 'uint256', - }, - ], - name: '_spendERC20Tokens', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, { inputs: [ { @@ -2314,18 +2290,6 @@ export class IZeroExContract extends BaseContract { stateMutability: 'nonpayable', type: 'function', }, - { - inputs: [], - name: 'getAllowanceTarget', - outputs: [ - { - name: 'target', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, { inputs: [ { @@ -3017,27 +2981,6 @@ export class IZeroExContract extends BaseContract { stateMutability: 'view', type: 'function', }, - { - inputs: [ - { - name: 'token', - type: 'address', - }, - { - name: 'owner', - type: 'address', - }, - ], - name: 'getSpendableERC20BalanceOf', - outputs: [ - { - name: 'amount', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, { inputs: [], name: 'getTransformWallet', @@ -3212,6 +3155,35 @@ export class IZeroExContract extends BaseContract { stateMutability: 'payable', type: 'function', }, + { + inputs: [ + { + name: 'tokens', + type: 'address[]', + }, + { + name: 'sellAmount', + type: 'uint256', + }, + { + name: 'minBuyAmount', + type: 'uint256', + }, + { + name: 'fork', + type: 'uint8', + }, + ], + name: 'sellToPancakeSwap', + outputs: [ + { + name: 'buyAmount', + type: 'uint256', + }, + ], + stateMutability: 'payable', + type: 'function', + }, { inputs: [ { @@ -3581,69 +3553,6 @@ export class IZeroExContract extends BaseContract { }, }; } - /** - * Transfers ERC20 tokens from `owner` to `to`. - * Only callable from within. - * @param token The token to spend. - * @param owner The owner of the tokens. - * @param to The recipient of the tokens. - * @param amount The amount of `token` to transfer. - */ - public _spendERC20Tokens(token: string, owner: string, to: string, amount: BigNumber): ContractTxFunctionObj { - const self = (this as any) as IZeroExContract; - assert.isString('token', token); - assert.isString('owner', owner); - assert.isString('to', to); - assert.isBigNumber('amount', amount); - const functionSignature = '_spendERC20Tokens(address,address,address,uint256)'; - - return { - async sendTransactionAsync( - txData?: Partial | undefined, - opts: SendTransactionOpts = { shouldValidate: true }, - ): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( - { data: this.getABIEncodedTransactionData(), ...txData }, - this.estimateGasAsync.bind(this), - ); - if (opts.shouldValidate !== false) { - await this.callAsync(txDataWithDefaults); - } - return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); - }, - awaitTransactionSuccessAsync( - txData?: Partial, - opts: AwaitTransactionSuccessOpts = { shouldValidate: true }, - ): PromiseWithTransactionHash { - return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts); - }, - async estimateGasAsync(txData?: Partial | undefined): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({ - data: this.getABIEncodedTransactionData(), - ...txData, - }); - return self._web3Wrapper.estimateGasAsync(txDataWithDefaults); - }, - async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { - BaseContract._assertCallParams(callData, defaultBlock); - const rawCallResult = await self._performCallAsync( - { data: this.getABIEncodedTransactionData(), ...callData }, - defaultBlock, - ); - const abiEncoder = self._lookupAbiEncoder(functionSignature); - BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); - return abiEncoder.strictDecodeReturnValue(rawCallResult); - }, - getABIEncodedTransactionData(): string { - return self._strictEncodeArguments(functionSignature, [ - token.toLowerCase(), - owner.toLowerCase(), - to.toLowerCase(), - amount, - ]); - }, - }; - } /** * Internal version of `transformERC20()`. Only callable from within. * @param args A `TransformERC20Args` struct. @@ -5169,55 +5078,6 @@ export class IZeroExContract extends BaseContract { }, }; } - /** - * Get the address of the allowance target. - */ - public getAllowanceTarget(): ContractTxFunctionObj { - const self = (this as any) as IZeroExContract; - const functionSignature = 'getAllowanceTarget()'; - - return { - async sendTransactionAsync( - txData?: Partial | undefined, - opts: SendTransactionOpts = { shouldValidate: true }, - ): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( - { data: this.getABIEncodedTransactionData(), ...txData }, - this.estimateGasAsync.bind(this), - ); - if (opts.shouldValidate !== false) { - await this.callAsync(txDataWithDefaults); - } - return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); - }, - awaitTransactionSuccessAsync( - txData?: Partial, - opts: AwaitTransactionSuccessOpts = { shouldValidate: true }, - ): PromiseWithTransactionHash { - return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts); - }, - async estimateGasAsync(txData?: Partial | undefined): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({ - data: this.getABIEncodedTransactionData(), - ...txData, - }); - return self._web3Wrapper.estimateGasAsync(txDataWithDefaults); - }, - async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { - BaseContract._assertCallParams(callData, defaultBlock); - const rawCallResult = await self._performCallAsync( - { data: this.getABIEncodedTransactionData(), ...callData }, - defaultBlock, - ); - const abiEncoder = self._lookupAbiEncoder(functionSignature); - BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); - return abiEncoder.strictDecodeReturnValue(rawCallResult); - }, - getABIEncodedTransactionData(): string { - return self._strictEncodeArguments(functionSignature, []); - }, - }; - } /** * Get the canonical hash of a limit order. * @param order The limit order. @@ -6020,60 +5880,6 @@ export class IZeroExContract extends BaseContract { }, }; } - /** - * Gets the maximum amount of an ERC20 token `token` that can be - * pulled from `owner`. - * @param token The token to spend. - * @param owner The owner of the tokens. - */ - public getSpendableERC20BalanceOf(token: string, owner: string): ContractTxFunctionObj { - const self = (this as any) as IZeroExContract; - assert.isString('token', token); - assert.isString('owner', owner); - const functionSignature = 'getSpendableERC20BalanceOf(address,address)'; - - return { - async sendTransactionAsync( - txData?: Partial | undefined, - opts: SendTransactionOpts = { shouldValidate: true }, - ): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( - { data: this.getABIEncodedTransactionData(), ...txData }, - this.estimateGasAsync.bind(this), - ); - if (opts.shouldValidate !== false) { - await this.callAsync(txDataWithDefaults); - } - return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); - }, - awaitTransactionSuccessAsync( - txData?: Partial, - opts: AwaitTransactionSuccessOpts = { shouldValidate: true }, - ): PromiseWithTransactionHash { - return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts); - }, - async estimateGasAsync(txData?: Partial | undefined): Promise { - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({ - data: this.getABIEncodedTransactionData(), - ...txData, - }); - return self._web3Wrapper.estimateGasAsync(txDataWithDefaults); - }, - async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { - BaseContract._assertCallParams(callData, defaultBlock); - const rawCallResult = await self._performCallAsync( - { data: this.getABIEncodedTransactionData(), ...callData }, - defaultBlock, - ); - const abiEncoder = self._lookupAbiEncoder(functionSignature); - BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); - return abiEncoder.strictDecodeReturnValue(rawCallResult); - }, - getABIEncodedTransactionData(): string { - return self._strictEncodeArguments(functionSignature, [token.toLowerCase(), owner.toLowerCase()]); - }, - }; - } /** * Return the current wallet instance that will serve as the execution * context for transformations. @@ -6536,6 +6342,68 @@ export class IZeroExContract extends BaseContract { }, }; } + /** + * Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap. + * @param tokens Sell path. + * @param sellAmount of `tokens[0]` Amount to sell. + * @param minBuyAmount Minimum amount of `tokens[-1]` to buy. + * @param fork The protocol fork to use. + */ + public sellToPancakeSwap( + tokens: string[], + sellAmount: BigNumber, + minBuyAmount: BigNumber, + fork: number | BigNumber, + ): ContractTxFunctionObj { + const self = (this as any) as IZeroExContract; + assert.isArray('tokens', tokens); + assert.isBigNumber('sellAmount', sellAmount); + assert.isBigNumber('minBuyAmount', minBuyAmount); + assert.isNumberOrBigNumber('fork', fork); + const functionSignature = 'sellToPancakeSwap(address[],uint256,uint256,uint8)'; + + return { + async sendTransactionAsync( + txData?: Partial | undefined, + opts: SendTransactionOpts = { shouldValidate: true }, + ): Promise { + const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( + { data: this.getABIEncodedTransactionData(), ...txData }, + this.estimateGasAsync.bind(this), + ); + if (opts.shouldValidate !== false) { + await this.callAsync(txDataWithDefaults); + } + return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); + }, + awaitTransactionSuccessAsync( + txData?: Partial, + opts: AwaitTransactionSuccessOpts = { shouldValidate: true }, + ): PromiseWithTransactionHash { + return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts); + }, + async estimateGasAsync(txData?: Partial | undefined): Promise { + const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({ + data: this.getABIEncodedTransactionData(), + ...txData, + }); + return self._web3Wrapper.estimateGasAsync(txDataWithDefaults); + }, + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { data: this.getABIEncodedTransactionData(), ...callData }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, [tokens, sellAmount, minBuyAmount, fork]); + }, + }; + } /** * Efficiently sell directly to uniswap/sushiswap. * @param tokens Sell path. diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index 782444bc4f..eb77c8857f 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "8.0.0", + "changes": [ + { + "note": "Remove exchangeProxyAllowanceTarget" + } + ] + }, { "timestamp": 1616005394, "version": "7.0.1", diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index b9d54c64ec..737b601051 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -315,7 +315,6 @@ export async function runMigrationsAsync( ); const exchangeProxy = await fullMigrateExchangeProxyAsync(txDefaults.from, provider, txDefaults); - const exchangeProxyAllowanceTargetAddress = await exchangeProxy.getAllowanceTarget().callAsync(); const exchangeProxyFlashWalletAddress = await exchangeProxy.getTransformWallet().callAsync(); // Deploy transformers. @@ -382,7 +381,6 @@ export async function runMigrationsAsync( dexForwarderBridge: NULL_ADDRESS, exchangeProxyGovernor: NULL_ADDRESS, exchangeProxy: exchangeProxy.address, - exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, exchangeProxyTransformerDeployer: txDefaults.from, exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress, exchangeProxyLiquidityProviderSandbox: NULL_ADDRESS, diff --git a/packages/protocol-utils/CHANGELOG.json b/packages/protocol-utils/CHANGELOG.json index 7dd7751ac8..c7800f3520 100644 --- a/packages/protocol-utils/CHANGELOG.json +++ b/packages/protocol-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.4.0", + "changes": [ + { + "note": "Added Nerve", + "pr": 181 + } + ] + }, { "timestamp": 1616005394, "version": "1.3.1", diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index c9aa84e85f..06ecd1bb33 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -1,11 +1,11 @@ -import { AbiEncoder, BigNumber, NULL_ADDRESS } from '@0x/utils'; +import { AbiEncoder, BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; import * as ethjs from 'ethereumjs-util'; import { LimitOrder, LimitOrderFields, RfqOrder, RfqOrderFields } from './orders'; import { Signature, SIGNATURE_ABI } from './signature_utils'; const BRIDGE_ORDER_ABI_COMPONENTS = [ - { name: 'source', type: 'uint256' }, + { name: 'source', type: 'bytes32' }, { name: 'takerTokenAmount', type: 'uint256' }, { name: 'makerTokenAmount', type: 'uint256' }, { name: 'bridgeData', type: 'bytes' }, @@ -104,38 +104,38 @@ export interface FillQuoteTransformerData { refundReceiver: string; } +// tslint:disable: enum-naming /** - * Identifies the DEX type of a bridge order. + * Identifies the DEX protocol used to fill a bridge order. */ -export enum BridgeSource { - Balancer, - Bancor, - // tslint:disable-next-line: enum-naming - CoFiX, +export enum BridgeProtocol { + Unknown, Curve, - Cream, - CryptoCom, - Dodo, + UniswapV2, + Uniswap, + Balancer, Kyber, - LiquidityProvider, Mooniswap, MStable, Oasis, Shell, - Snowswap, - Sushiswap, - Swerve, - Uniswap, - UniswapV2, + Dodo, DodoV2, - Linkswap, + CryptoCom, + Bancor, + CoFiX, + Nerve, } +// tslint:enable: enum-naming /** * `FillQuoteTransformer.BridgeOrder` */ export interface FillQuoteTransformerBridgeOrder { - source: BridgeSource; + // A bytes32 hex where the upper 16 bytes are an int128, right-aligned + // protocol ID and the lower 16 bytes are a bytes16, left-aligned, + // ASCII source name. + source: string; takerTokenAmount: BigNumber; makerTokenAmount: BigNumber; bridgeData: string; @@ -353,3 +353,17 @@ export function encodePositiveSlippageFeeTransformerData(data: PositiveSlippageF export function decodePositiveSlippageFeeTransformerData(encoded: string): PositiveSlippageFeeTransformerData { return positiveSlippageFeeTransformerDataEncoder.decode(encoded); } + +/** + * Packs a bridge protocol ID and an ASCII DEX name into a single byte32. + */ +export function encodeBridgeSourceId(protocol: BridgeProtocol, name: string): string { + const nameBuf = Buffer.from(name); + if (nameBuf.length > 16) { + throw new Error(`"${name}" is too long to be a bridge source name (max of 16 ascii chars)`); + } + return hexUtils.concat( + hexUtils.leftPad(hexUtils.toHex(protocol), 16), + hexUtils.rightPad(hexUtils.toHex(Buffer.from(name)), 16), + ); +}