diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index c9d3ed0245..9e854815f8 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -31,6 +31,7 @@ import "./mixins/MixinCryptoCom.sol"; import "./mixins/MixinDodo.sol"; import "./mixins/MixinDodoV2.sol"; import "./mixins/MixinKyber.sol"; +import "./mixins/MixinKyberDmm.sol"; import "./mixins/MixinMakerPSM.sol"; import "./mixins/MixinMooniswap.sol"; import "./mixins/MixinMStable.sol"; @@ -53,6 +54,7 @@ contract BridgeAdapter is MixinDodo, MixinDodoV2, MixinKyber, + MixinKyberDmm, MixinMakerPSM, MixinMooniswap, MixinMStable, @@ -217,6 +219,12 @@ contract BridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.KYBERDMM) { + boughtAmount = _tradeKyberDmm( + buyToken, + 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 index 253db4bd41..01135e5cd4 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -46,4 +46,5 @@ library BridgeProtocols { uint128 internal constant MAKERPSM = 16; uint128 internal constant BALANCERV2 = 17; uint128 internal constant UNISWAPV3 = 18; + uint128 internal constant KYBERDMM = 19; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol new file mode 100644 index 0000000000..6819c932a6 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol @@ -0,0 +1,94 @@ +// 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/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "../IBridgeAdapter.sol"; + +/* + KyberDmm Router +*/ +interface IKyberDmmRouter { + + /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path. + /// The first element of path is the input token, the last is the output token, and any intermediate elements represent + /// intermediate pairs to trade through (if, for example, a direct pair does not exist). + /// @param amountIn The amount of input tokens to send. + /// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert. + /// @param pools An array of pool addresses. pools.length must be >= 1. + /// @param path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity. + /// @param to Recipient of the output tokens. + /// @param deadline Unix timestamp after which the transaction will revert. + /// @return amounts The input token amount and all subsequent output token amounts. + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata pools, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); +} + +contract MixinKyberDmm { + + using LibERC20TokenV06 for IERC20TokenV06; + + function _tradeKyberDmm( + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) + internal + returns (uint256 boughtAmount) + { + address router; + address[] memory pools; + address[] memory path; + (router, pools, path) = abi.decode(bridgeData, (address, address[], address[])); + + require(pools.length >= 1, "MixinKyberDmm/POOLS_LENGTH_MUST_BE_AT_LEAST_ONE"); + require(path.length == pools.length + 1, "MixinKyberDmm/ARRAY_LENGTH_MISMATCH"); + require( + path[path.length - 1] == address(buyToken), + "MixinKyberDmm/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" + ); + // Grant the KyberDmm router an allowance to sell the first token. + IERC20TokenV06(path[0]).approveIfBelow(address(router), sellAmount); + + uint[] memory amounts = IKyberDmmRouter(router).swapExactTokensForTokens( + // Sell all tokens we hold. + sellAmount, + // Minimum buy amount. + 1, + pools, + // 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/src/transformers/bridges/mixins/MixinUniswapV2.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol index f0ff35f707..79f859f3d2 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol @@ -69,7 +69,7 @@ contract MixinUniswapV2 { assembly { path := _path } } - require(path.length >= 2, "MixinUniswapV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); + require(path.length >= 2, "MixinUniswapV2/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); require( path[path.length - 1] == buyToken, "MixinUniswapV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN" diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index 6599d08cdb..f4b41977ab 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -43,7 +43,7 @@ "config": { "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|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|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|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|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|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|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|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|TestOrderSignerRegistryWithContractWallet|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/test/artifacts.ts b/contracts/zero-ex/test/artifacts.ts index 5cb0702e79..1be41229f2 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -84,6 +84,7 @@ import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json'; import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json'; import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json'; import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json'; +import * as MixinKyberDmm from '../test/generated-artifacts/MixinKyberDmm.json'; import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json'; import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json'; import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json'; @@ -245,6 +246,7 @@ export const artifacts = { MixinDodo: MixinDodo as ContractArtifact, MixinDodoV2: MixinDodoV2 as ContractArtifact, MixinKyber: MixinKyber as ContractArtifact, + MixinKyberDmm: MixinKyberDmm as ContractArtifact, MixinMStable: MixinMStable as ContractArtifact, MixinMakerPSM: MixinMakerPSM as ContractArtifact, MixinMooniswap: MixinMooniswap as ContractArtifact, diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index e8698ae140..845cd19452 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -82,6 +82,7 @@ export * from '../test/generated-wrappers/mixin_curve'; export * from '../test/generated-wrappers/mixin_dodo'; export * from '../test/generated-wrappers/mixin_dodo_v2'; export * from '../test/generated-wrappers/mixin_kyber'; +export * from '../test/generated-wrappers/mixin_kyber_dmm'; export * from '../test/generated-wrappers/mixin_m_stable'; export * from '../test/generated-wrappers/mixin_maker_p_s_m'; export * from '../test/generated-wrappers/mixin_mooniswap'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index bce9df73e3..5c9bd5d54a 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -113,6 +113,7 @@ "test/generated-artifacts/MixinDodo.json", "test/generated-artifacts/MixinDodoV2.json", "test/generated-artifacts/MixinKyber.json", + "test/generated-artifacts/MixinKyberDmm.json", "test/generated-artifacts/MixinMStable.json", "test/generated-artifacts/MixinMakerPSM.json", "test/generated-artifacts/MixinMooniswap.json", diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 160b4ac7c2..5c18bce154 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "6.15.0", + "changes": [ + { + "note": "Fix KyberDmm", + "pr": 236 + } + ] + }, { "version": "6.14.0", "changes": [ diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index bcd9da21c1..fb500604a0 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -28,6 +28,7 @@ import "./DODOSampler.sol"; import "./DODOV2Sampler.sol"; import "./Eth2DaiSampler.sol"; import "./KyberSampler.sol"; +import "./KyberDmmSampler.sol"; import "./LiquidityProviderSampler.sol"; import "./MakerPSMSampler.sol"; import "./MultiBridgeSampler.sol"; @@ -52,6 +53,7 @@ contract ERC20BridgeSampler is DODOV2Sampler, Eth2DaiSampler, KyberSampler, + KyberDmmSampler, LiquidityProviderSampler, MakerPSMSampler, MStableSampler, diff --git a/packages/asset-swapper/contracts/src/KyberDmmSampler.sol b/packages/asset-swapper/contracts/src/KyberDmmSampler.sol new file mode 100644 index 0000000000..7182232326 --- /dev/null +++ b/packages/asset-swapper/contracts/src/KyberDmmSampler.sol @@ -0,0 +1,159 @@ +// 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; + +interface IKyberDmmFactory { + + function getPoolAtIndex(address token0, address token1, uint256 index) + external + view + returns (address); +} + +interface IKyberDmmRouter { + + function factory() external view returns (address); + + function getAmountsOut(uint256 amountIn, address[] calldata pools, address[] calldata path) + external + view + returns (uint256[] memory amounts); + + function getAmountsIn(uint256 amountOut, address[] calldata pools, address[] calldata path) + external + view + returns (uint256[] memory amounts); +} + + + +contract KyberDmmSampler +{ + /// @dev Gas limit for KyberDmm calls. + uint256 constant private KYBER_DMM_CALL_GAS = 150e3; // 150k + + /// @dev Sample sell quotes from KyberDmm. + /// @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 pools The pool addresses involved in the multi path trade + /// @return makerTokenAmounts Maker amounts bought at each taker token + /// amount. + function sampleSellsFromKyberDmm( + address router, + address[] memory path, + uint256[] memory takerTokenAmounts + ) + public + view + returns (address[] memory pools, uint256[] memory makerTokenAmounts) + { + uint256 numSamples = takerTokenAmounts.length; + makerTokenAmounts = new uint256[](numSamples); + pools = _getKyberDmmPools(router, path); + if (pools.length == 0) { + return (pools, makerTokenAmounts); + } + for (uint256 i = 0; i < numSamples; i++) { + try + IKyberDmmRouter(router).getAmountsOut + {gas: KYBER_DMM_CALL_GAS} + (takerTokenAmounts[i], pools, path) + 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; + } + } + } + + /// @dev Sample buy quotes from KyberDmm. + /// @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 pools The pool addresses involved in the multi path trade + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromKyberDmm( + address router, + address[] memory path, + uint256[] memory makerTokenAmounts + ) + public + view + returns (address[] memory pools, uint256[] memory takerTokenAmounts) + { + uint256 numSamples = makerTokenAmounts.length; + takerTokenAmounts = new uint256[](numSamples); + pools = _getKyberDmmPools(router, path); + if (pools.length == 0) { + return (pools, takerTokenAmounts); + } + for (uint256 i = 0; i < numSamples; i++) { + try + IKyberDmmRouter(router).getAmountsIn + {gas: KYBER_DMM_CALL_GAS} + (makerTokenAmounts[i], pools, path) + 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; + } + } + } + + function _getKyberDmmPools( + address router, + address[] memory path + ) + private + view + returns (address[] memory pools) + { + pools = new address[](path.length - 1); + IKyberDmmFactory factory = IKyberDmmFactory(IKyberDmmRouter(router).factory()); + for (uint256 i = 0; i < pools.length; i++) { + // Currently only supporting the first pool found at the index + try + factory.getPoolAtIndex + {gas: KYBER_DMM_CALL_GAS} + (path[i], path[i + 1], 0) + returns (address pool) + { + pools[i] = pool; + } catch (bytes memory) { + return new address[](0); + } + } + } +} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 6c0ce954d7..9fed4795b7 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -39,7 +39,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|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json", "postpublish": { "assets": [] } diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index eb5c11f688..1e76bfb79b 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -125,7 +125,7 @@ export class SwapQuoter { { block: BlockParamLiteral.Latest, overrides: defaultCodeOverrides }, options.samplerOverrides, ); - const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[]); + const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[], { BigNumber }); const samplerContract = new ERC20BridgeSamplerContract( samplerAddress, this.provider, 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 c1f13b90b6..feec272679 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 @@ -14,7 +14,6 @@ import { JULSWAP_ROUTER_BY_CHAIN_ID, KYBER_BANNED_RESERVES, KYBER_BRIDGED_LIQUIDITY_PREFIX, - KYBER_DMM_ROUTER_BY_CHAIN_ID, MAX_DODOV2_POOLS_QUERIED, MAX_KYBER_RESERVES_QUERIED, MSTABLE_POOLS_BY_CHAIN_ID, @@ -314,7 +313,6 @@ export function uniswapV2LikeRouterAddress( | ERC20BridgeSource.PancakeSwap | ERC20BridgeSource.PancakeSwapV2 | ERC20BridgeSource.BakerySwap - | ERC20BridgeSource.KyberDmm | ERC20BridgeSource.ApeSwap | ERC20BridgeSource.CafeSwap | ERC20BridgeSource.CheeseSwap @@ -333,8 +331,6 @@ export function uniswapV2LikeRouterAddress( return PANCAKESWAPV2_ROUTER_BY_CHAIN_ID[chainId]; case ERC20BridgeSource.BakerySwap: return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId]; - case ERC20BridgeSource.KyberDmm: - return KYBER_DMM_ROUTER_BY_CHAIN_ID[chainId]; case ERC20BridgeSource.ApeSwap: return APESWAP_ROUTER_BY_CHAIN_ID[chainId]; case ERC20BridgeSource.CafeSwap: 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 f9f6214ad4..078ca78e4d 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -85,7 +85,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.CryptoCom, ERC20BridgeSource.Linkswap, ERC20BridgeSource.MakerPsm, - ERC20BridgeSource.KyberDmm, + // ERC20BridgeSource.KyberDmm, ERC20BridgeSource.Smoothy, ERC20BridgeSource.Component, ERC20BridgeSource.Saddle, @@ -158,7 +158,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.CryptoCom, ERC20BridgeSource.Linkswap, ERC20BridgeSource.MakerPsm, - ERC20BridgeSource.KyberDmm, + // ERC20BridgeSource.KyberDmm, ERC20BridgeSource.Smoothy, ERC20BridgeSource.Component, ERC20BridgeSource.Saddle, @@ -941,7 +941,7 @@ export const OASIS_ROUTER_BY_CHAIN_ID = valueByChainId( export const KYBER_DMM_ROUTER_BY_CHAIN_ID = valueByChainId( { - [ChainId.Mainnet]: '0x12807818B584a3Fa65D38B6C25B13983fE888D6E', + [ChainId.Mainnet]: '0x1c87257f5e8609940bc751a07bb085bb7f8cdbe6', }, NULL_ADDRESS, ); 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 65da78bb8d..14d32fd404 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -16,6 +16,7 @@ import { ERC20BridgeSource, FillData, GenericRouterFillData, + KyberDmmFillData, KyberFillData, LiquidityProviderFillData, MakerPsmFillData, @@ -99,8 +100,6 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo'); case ERC20BridgeSource.Kyber: return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber'); - case ERC20BridgeSource.KyberDmm: - return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'KyberDmm'); case ERC20BridgeSource.LiquidityProvider: // "LiquidityProvider" is too long to encode (17 characters). return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP'); @@ -158,6 +157,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JulSwap'); case ERC20BridgeSource.UniswapV3: return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3'); + case ERC20BridgeSource.KyberDmm: + return encodeBridgeSourceId(BridgeProtocol.KyberDmm, 'KyberDmm'); default: throw new Error(AggregationError.NoBridgeForSource); } @@ -217,7 +218,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder case ERC20BridgeSource.PancakeSwap: case ERC20BridgeSource.PancakeSwapV2: case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.KyberDmm: case ERC20BridgeSource.ApeSwap: case ERC20BridgeSource.CafeSwap: case ERC20BridgeSource.CheeseSwap: @@ -274,6 +274,14 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder const uniswapV3FillData = (order as OptimizedMarketBridgeOrder).fillData; bridgeData = encoder.encode([uniswapV3FillData.router, uniswapV3FillData.uniswapPath]); break; + case ERC20BridgeSource.KyberDmm: + const kyberDmmFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([ + kyberDmmFillData.router, + kyberDmmFillData.poolsPath, + kyberDmmFillData.tokenAddressPath, + ]); + break; default: throw new Error(AggregationError.NoBridgeForSource); } @@ -390,7 +398,6 @@ export const BRIDGE_ENCODERS: { [ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder, [ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder, [ERC20BridgeSource.Linkswap]: routerAddressPathEncoder, - [ERC20BridgeSource.KyberDmm]: routerAddressPathEncoder, // BSC [ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder, [ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder, @@ -415,6 +422,7 @@ export const BRIDGE_ENCODERS: { { name: 'router', type: 'address' }, { name: 'path', type: 'bytes' }, ]), + [ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'), }; function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] { 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 21f9ae6e63..db3e92288c 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 @@ -23,6 +23,7 @@ import { DODO_CONFIG_BY_CHAIN_ID, DODOV2_FACTORIES_BY_CHAIN_ID, KYBER_CONFIG_BY_CHAIN_ID, + KYBER_DMM_ROUTER_BY_CHAIN_ID, LINKSWAP_ROUTER_BY_CHAIN_ID, LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID, MAINNET_TOKENS, @@ -56,6 +57,7 @@ import { ERC20BridgeSource, GenericRouterFillData, HopInfo, + KyberDmmFillData, KyberFillData, KyberSamplerOpts, LiquidityProviderFillData, @@ -236,6 +238,52 @@ export class SamplerOperations { }); } + public getKyberDmmSellQuotes( + router: string, + tokenAddressPath: string[], + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.KyberDmm, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromKyberDmm, + params: [router, tokenAddressPath, takerFillAmounts], + callback: (callResults: string, fillData: KyberDmmFillData): BigNumber[] => { + const [pools, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>( + 'sampleSellsFromKyberDmm', + callResults, + ); + fillData.poolsPath = pools; + fillData.router = router; + fillData.tokenAddressPath = tokenAddressPath; + return samples; + }, + }); + } + + public getKyberDmmBuyQuotes( + router: string, + tokenAddressPath: string[], + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.KyberDmm, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromKyberDmm, + params: [router, tokenAddressPath, makerFillAmounts], + callback: (callResults: string, fillData: KyberDmmFillData): BigNumber[] => { + const [pools, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>( + 'sampleBuysFromKyberDmm', + callResults, + ); + fillData.poolsPath = pools; + fillData.router = router; + fillData.tokenAddressPath = tokenAddressPath; + return samples; + }, + }); + } + public getUniswapSellQuotes( router: string, makerToken: string, @@ -1134,7 +1182,6 @@ export class SamplerOperations { case ERC20BridgeSource.PancakeSwap: case ERC20BridgeSource.PancakeSwapV2: case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.KyberDmm: case ERC20BridgeSource.ApeSwap: case ERC20BridgeSource.CafeSwap: case ERC20BridgeSource.CheeseSwap: @@ -1147,6 +1194,16 @@ export class SamplerOperations { [takerToken, makerToken], ...intermediateTokens.map(t => [takerToken, t, makerToken]), ].map(path => this.getUniswapV2SellQuotes(uniLikeRouter, path, takerFillAmounts, source)); + case ERC20BridgeSource.KyberDmm: + const kyberDmmRouter = KYBER_DMM_ROUTER_BY_CHAIN_ID[this.chainId]; + if (!isValidAddress(kyberDmmRouter)) { + return []; + } + return this.getKyberDmmSellQuotes( + kyberDmmRouter, + [takerToken, makerToken], + takerFillAmounts, + ); case ERC20BridgeSource.Kyber: return getKyberOffsets().map(offset => this.getKyberSellQuotes( @@ -1381,7 +1438,6 @@ export class SamplerOperations { case ERC20BridgeSource.PancakeSwap: case ERC20BridgeSource.PancakeSwapV2: case ERC20BridgeSource.BakerySwap: - case ERC20BridgeSource.KyberDmm: case ERC20BridgeSource.ApeSwap: case ERC20BridgeSource.CafeSwap: case ERC20BridgeSource.CheeseSwap: @@ -1394,6 +1450,16 @@ export class SamplerOperations { [takerToken, makerToken], ...intermediateTokens.map(t => [takerToken, t, makerToken]), ].map(path => this.getUniswapV2BuyQuotes(uniLikeRouter, path, makerFillAmounts, source)); + case ERC20BridgeSource.KyberDmm: + const kyberDmmRouter = KYBER_DMM_ROUTER_BY_CHAIN_ID[this.chainId]; + if (!isValidAddress(kyberDmmRouter)) { + return []; + } + return this.getKyberDmmBuyQuotes( + kyberDmmRouter, + [takerToken, makerToken], + makerFillAmounts, + ); case ERC20BridgeSource.Kyber: return getKyberOffsets().map(offset => this.getKyberBuyQuotes( 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 770673f6d5..3054aa791d 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -224,6 +224,10 @@ export interface UniswapV3FillData extends FillData { pathAmounts: Array<{ uniswapPath: string; inputAmount: BigNumber }>; } +export interface KyberDmmFillData extends UniswapV2FillData { + poolsPath: string[]; +} + /** * Represents a node on a fill path. */ diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index e6af9fb93a..c8b4b4c7a4 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -29,6 +29,7 @@ import * as IShell from '../test/generated-artifacts/IShell.json'; import * as ISmoothy from '../test/generated-artifacts/ISmoothy.json'; import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json'; import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json'; +import * as KyberDmmSampler from '../test/generated-artifacts/KyberDmmSampler.json'; import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json'; import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.json'; import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json'; @@ -58,6 +59,7 @@ export const artifacts = { ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, Eth2DaiSampler: Eth2DaiSampler as ContractArtifact, FakeTaker: FakeTaker as ContractArtifact, + KyberDmmSampler: KyberDmmSampler as ContractArtifact, KyberSampler: KyberSampler as ContractArtifact, LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact, MStableSampler: MStableSampler as ContractArtifact, diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index d678f4a5c3..ebbd2eea6e 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -395,7 +395,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Uniswap]: { router: randomAddress() }, [ERC20BridgeSource.Eth2Dai]: { router: randomAddress() }, [ERC20BridgeSource.MakerPsm]: {}, - [ERC20BridgeSource.KyberDmm]: { tokenAddressPath: [] }, + [ERC20BridgeSource.KyberDmm]: { tokenAddressPath: [], router: randomAddress(), poolsPath: [] }, }; const DEFAULT_OPS = { diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index 92feca1f15..611e70949a 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -27,6 +27,7 @@ export * from '../test/generated-wrappers/i_shell'; export * from '../test/generated-wrappers/i_smoothy'; export * from '../test/generated-wrappers/i_uniswap_exchange_quotes'; export * from '../test/generated-wrappers/i_uniswap_v2_router01'; +export * from '../test/generated-wrappers/kyber_dmm_sampler'; export * from '../test/generated-wrappers/kyber_sampler'; export * from '../test/generated-wrappers/liquidity_provider_sampler'; export * from '../test/generated-wrappers/m_stable_sampler'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json index 5d9a9ee254..63cf34fe5b 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -30,6 +30,7 @@ "test/generated-artifacts/ISmoothy.json", "test/generated-artifacts/IUniswapExchangeQuotes.json", "test/generated-artifacts/IUniswapV2Router01.json", + "test/generated-artifacts/KyberDmmSampler.json", "test/generated-artifacts/KyberSampler.json", "test/generated-artifacts/LiquidityProviderSampler.json", "test/generated-artifacts/MStableSampler.json", diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index 7927092d9d..5a0c4c1e4d 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -128,6 +128,7 @@ export enum BridgeProtocol { MakerPsm, BalancerV2, UniswapV3, + KyberDmm, } // tslint:enable: enum-naming