From cc31445189f38bf511605e33efdb0c04ca14d409 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 24 Aug 2020 11:21:01 +1000 Subject: [PATCH] feat: asset-swapper mooniswap (#2675) * feat: asset-swapper sampler early exit * feat: asset-swapper mooniswap * tests and linter * deploy to mainnet * CHANGELOGs * fix excluded sources difference * typo --- contracts/asset-proxy/CHANGELOG.json | 4 + .../contracts/src/bridges/MooniswapBridge.sol | 149 ++++++++++++++++++ .../contracts/src/interfaces/IMooniswap.sol | 40 +++++ contracts/asset-proxy/package.json | 2 +- contracts/asset-proxy/src/artifacts.ts | 4 + contracts/asset-proxy/src/wrappers.ts | 2 + contracts/asset-proxy/test/artifacts.ts | 4 + contracts/asset-proxy/test/wrappers.ts | 2 + contracts/asset-proxy/tsconfig.json | 4 + .../contracts/src/DeploymentConstants.sol | 12 ++ packages/asset-swapper/CHANGELOG.json | 4 + .../contracts/src/CurveSampler.sol | 8 +- .../contracts/src/ERC20BridgeSampler.sol | 2 + .../contracts/src/Eth2DaiSampler.sol | 41 +---- .../contracts/src/IMooniswap.sol | 37 +++++ .../src/LiquidityProviderSampler.sol | 5 +- .../contracts/src/MStableSampler.sol | 4 +- .../contracts/src/MooniswapSampler.sol | 146 +++++++++++++++++ .../contracts/src/MultiBridgeSampler.sol | 6 +- packages/asset-swapper/package.json | 3 +- packages/asset-swapper/src/swap_quoter.ts | 4 +- .../utils/market_operation_utils/constants.ts | 9 +- .../src/utils/market_operation_utils/index.ts | 8 +- .../utils/market_operation_utils/orders.ts | 2 + .../sampler_operations.ts | 50 +++++- .../src/utils/market_operation_utils/types.ts | 1 + packages/asset-swapper/test/artifacts.ts | 4 + .../test/market_operation_utils_test.ts | 5 + packages/asset-swapper/test/wrappers.ts | 2 + packages/asset-swapper/tsconfig.json | 2 + packages/contract-addresses/CHANGELOG.json | 4 + packages/contract-addresses/addresses.json | 5 + packages/contract-addresses/src/index.ts | 1 + packages/migrations/src/migration.ts | 1 + 34 files changed, 519 insertions(+), 58 deletions(-) create mode 100644 contracts/asset-proxy/contracts/src/bridges/MooniswapBridge.sol create mode 100644 contracts/asset-proxy/contracts/src/interfaces/IMooniswap.sol create mode 100644 packages/asset-swapper/contracts/src/IMooniswap.sol create mode 100644 packages/asset-swapper/contracts/src/MooniswapSampler.sol diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index ca27fa23de..7b6ec1166a 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -17,6 +17,10 @@ { "note": "Added `MStableBridge`", "pr": 2662 + }, + { + "note": "Added `MooniswapBridge`", + "pr": 2675 } ] }, diff --git a/contracts/asset-proxy/contracts/src/bridges/MooniswapBridge.sol b/contracts/asset-proxy/contracts/src/bridges/MooniswapBridge.sol new file mode 100644 index 0000000000..753ef484af --- /dev/null +++ b/contracts/asset-proxy/contracts/src/bridges/MooniswapBridge.sol @@ -0,0 +1,149 @@ +/* + + Copyright 2020 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; +import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; +import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol"; +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; +import "../interfaces/IERC20Bridge.sol"; +import "../interfaces/IMooniswap.sol"; + + +// solhint-disable space-after-comma +// solhint-disable not-rely-on-time +contract MooniswapBridge is + IERC20Bridge, + IWallet, + DeploymentConstants +{ + + struct TransferState { + IMooniswap pool; + uint256 fromTokenBalance; + IEtherToken weth; + uint256 boughtAmount; + address fromTokenAddress; + address toTokenAddress; + } + + // solhint-disable no-empty-blocks + /// @dev Payable fallback to receive ETH from uniswap. + function () + external + payable + {} + + /// @dev Callback for `IERC20Bridge`. Tries to buy `amount` of + /// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress` + /// token encoded in the bridge data. + /// @param toTokenAddress The token to buy and transfer to `to`. + /// @param from The maker (this contract). + /// @param to The recipient of the bought tokens. + /// @param amount Minimum amount of `toTokenAddress` tokens to buy. + /// @param bridgeData The abi-encoded path of token addresses. Last element must be toTokenAddress + /// @return success The magic bytes if successful. + function bridgeTransferFrom( + address toTokenAddress, + address from, + address to, + uint256 amount, + bytes calldata bridgeData + ) + external + returns (bytes4 success) + { + // State memory object to avoid stack overflows. + TransferState memory state; + // Decode the bridge data to get the `fromTokenAddress`. + address fromTokenAddress = abi.decode(bridgeData, (address)); + // Get the weth contract. + state.weth = IEtherToken(_getWethAddress()); + // Get our balance of `fromTokenAddress` token. + state.fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this)); + + state.fromTokenAddress = fromTokenAddress == address(state.weth) ? address(0) : fromTokenAddress; + state.toTokenAddress = toTokenAddress == address(state.weth) ? address(0) : toTokenAddress; + state.pool = IMooniswap( + IMooniswapRegistry(_getMooniswapAddress()).pools( + state.fromTokenAddress, + state.toTokenAddress + ) + ); + + // withdraw WETH to ETH + if (state.fromTokenAddress == address(0)) { + state.weth.withdraw(state.fromTokenBalance); + } else { + // Grant the pool an allowance. + LibERC20Token.approveIfBelow( + state.fromTokenAddress, + address(state.pool), + state.fromTokenBalance + ); + } + uint256 ethValue = state.fromTokenAddress == address(0) ? state.fromTokenBalance : 0; + state.boughtAmount = state.pool.swap.value(ethValue)( + state.fromTokenAddress, + state.toTokenAddress, + state.fromTokenBalance, + amount, + address(0) + ); + // Deposit to WETH + if (state.toTokenAddress == address(0)) { + state.weth.deposit.value(state.boughtAmount)(); + } + + // Transfer funds to `to` + IERC20Token(toTokenAddress).transfer(to, state.boughtAmount); + + + emit ERC20BridgeTransfer( + // input token + fromTokenAddress, + // output token + toTokenAddress, + // input token amount + state.fromTokenBalance, + // output token amount + state.boughtAmount, + from, + to + ); + + return BRIDGE_SUCCESS; + } + + /// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker + /// and sign for itself in orders. Always succeeds. + /// @return magicValue Success bytes, always. + function isValidSignature( + bytes32, + bytes calldata + ) + external + view + returns (bytes4 magicValue) + { + return LEGACY_WALLET_MAGIC_VALUE; + } +} diff --git a/contracts/asset-proxy/contracts/src/interfaces/IMooniswap.sol b/contracts/asset-proxy/contracts/src/interfaces/IMooniswap.sol new file mode 100644 index 0000000000..daef84086c --- /dev/null +++ b/contracts/asset-proxy/contracts/src/interfaces/IMooniswap.sol @@ -0,0 +1,40 @@ +/* + + Copyright 2020 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; + + +interface IMooniswapRegistry { + + function pools(address token1, address token2) external view returns(address); +} + + +interface IMooniswap { + + function swap( + address fromToken, + address destToken, + uint256 amount, + uint256 minReturn, + address referral + ) + external + payable + returns(uint256 returnAmount); +} diff --git a/contracts/asset-proxy/package.json b/contracts/asset-proxy/package.json index b274e4ff3f..afb53323b9 100644 --- a/contracts/asset-proxy/package.json +++ b/contracts/asset-proxy/package.json @@ -38,7 +38,7 @@ "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { - "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MultiAssetProxy|Ownable|StaticCallProxy|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", + "abis": "./test/generated-artifacts/@(BalancerBridge|BancorBridge|ChaiBridge|CurveBridge|DexForwarderBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IBalancerPool|IBancorNetwork|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IGasToken|IKyberNetworkProxy|IMStable|IMooniswap|IUniswapExchange|IUniswapExchangeFactory|IUniswapV2Router01|KyberBridge|MStableBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MixinGasToken|MooniswapBridge|MultiAssetProxy|Ownable|StaticCallProxy|TestBancorBridge|TestChaiBridge|TestDexForwarderBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|TestUniswapV2Bridge|UniswapBridge|UniswapV2Bridge).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/asset-proxy/src/artifacts.ts b/contracts/asset-proxy/src/artifacts.ts index 2578197d61..52c80962c5 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -30,6 +30,7 @@ import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json'; import * as IEth2Dai from '../generated-artifacts/IEth2Dai.json'; import * as IGasToken from '../generated-artifacts/IGasToken.json'; import * as IKyberNetworkProxy from '../generated-artifacts/IKyberNetworkProxy.json'; +import * as IMooniswap from '../generated-artifacts/IMooniswap.json'; import * as IMStable from '../generated-artifacts/IMStable.json'; import * as IUniswapExchange from '../generated-artifacts/IUniswapExchange.json'; import * as IUniswapExchangeFactory from '../generated-artifacts/IUniswapExchangeFactory.json'; @@ -38,6 +39,7 @@ import * as KyberBridge from '../generated-artifacts/KyberBridge.json'; import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json'; import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json'; import * as MixinGasToken from '../generated-artifacts/MixinGasToken.json'; +import * as MooniswapBridge from '../generated-artifacts/MooniswapBridge.json'; import * as MStableBridge from '../generated-artifacts/MStableBridge.json'; import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json'; import * as Ownable from '../generated-artifacts/Ownable.json'; @@ -74,6 +76,7 @@ export const artifacts = { KyberBridge: KyberBridge as ContractArtifact, MStableBridge: MStableBridge as ContractArtifact, MixinGasToken: MixinGasToken as ContractArtifact, + MooniswapBridge: MooniswapBridge as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact, UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, IAssetData: IAssetData as ContractArtifact, @@ -91,6 +94,7 @@ export const artifacts = { IGasToken: IGasToken as ContractArtifact, IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, IMStable: IMStable as ContractArtifact, + IMooniswap: IMooniswap as ContractArtifact, IUniswapExchange: IUniswapExchange as ContractArtifact, IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact, IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index b1f6905913..caa4d97881 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -29,6 +29,7 @@ export * from '../generated-wrappers/i_eth2_dai'; export * from '../generated-wrappers/i_gas_token'; export * from '../generated-wrappers/i_kyber_network_proxy'; export * from '../generated-wrappers/i_m_stable'; +export * from '../generated-wrappers/i_mooniswap'; export * from '../generated-wrappers/i_uniswap_exchange'; export * from '../generated-wrappers/i_uniswap_exchange_factory'; export * from '../generated-wrappers/i_uniswap_v2_router01'; @@ -37,6 +38,7 @@ export * from '../generated-wrappers/m_stable_bridge'; export * from '../generated-wrappers/mixin_asset_proxy_dispatcher'; export * from '../generated-wrappers/mixin_authorizable'; export * from '../generated-wrappers/mixin_gas_token'; +export * from '../generated-wrappers/mooniswap_bridge'; export * from '../generated-wrappers/multi_asset_proxy'; export * from '../generated-wrappers/ownable'; export * from '../generated-wrappers/static_call_proxy'; diff --git a/contracts/asset-proxy/test/artifacts.ts b/contracts/asset-proxy/test/artifacts.ts index 2cacee3e89..f932ede72f 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -30,6 +30,7 @@ import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; import * as IGasToken from '../test/generated-artifacts/IGasToken.json'; import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json'; +import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; import * as IMStable from '../test/generated-artifacts/IMStable.json'; import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json'; import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json'; @@ -38,6 +39,7 @@ import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json'; import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json'; import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json'; import * as MixinGasToken from '../test/generated-artifacts/MixinGasToken.json'; +import * as MooniswapBridge from '../test/generated-artifacts/MooniswapBridge.json'; import * as MStableBridge from '../test/generated-artifacts/MStableBridge.json'; import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json'; import * as Ownable from '../test/generated-artifacts/Ownable.json'; @@ -74,6 +76,7 @@ export const artifacts = { KyberBridge: KyberBridge as ContractArtifact, MStableBridge: MStableBridge as ContractArtifact, MixinGasToken: MixinGasToken as ContractArtifact, + MooniswapBridge: MooniswapBridge as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact, UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, IAssetData: IAssetData as ContractArtifact, @@ -91,6 +94,7 @@ export const artifacts = { IGasToken: IGasToken as ContractArtifact, IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact, IMStable: IMStable as ContractArtifact, + IMooniswap: IMooniswap as ContractArtifact, IUniswapExchange: IUniswapExchange as ContractArtifact, IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact, IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, diff --git a/contracts/asset-proxy/test/wrappers.ts b/contracts/asset-proxy/test/wrappers.ts index 814197df86..d1328370d8 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -29,6 +29,7 @@ export * from '../test/generated-wrappers/i_eth2_dai'; export * from '../test/generated-wrappers/i_gas_token'; export * from '../test/generated-wrappers/i_kyber_network_proxy'; export * from '../test/generated-wrappers/i_m_stable'; +export * from '../test/generated-wrappers/i_mooniswap'; export * from '../test/generated-wrappers/i_uniswap_exchange'; export * from '../test/generated-wrappers/i_uniswap_exchange_factory'; export * from '../test/generated-wrappers/i_uniswap_v2_router01'; @@ -37,6 +38,7 @@ export * from '../test/generated-wrappers/m_stable_bridge'; export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher'; export * from '../test/generated-wrappers/mixin_authorizable'; export * from '../test/generated-wrappers/mixin_gas_token'; +export * from '../test/generated-wrappers/mooniswap_bridge'; export * from '../test/generated-wrappers/multi_asset_proxy'; export * from '../test/generated-wrappers/ownable'; export * from '../test/generated-wrappers/static_call_proxy'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index 3fb54954f9..a8072a6556 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -29,6 +29,7 @@ "generated-artifacts/IGasToken.json", "generated-artifacts/IKyberNetworkProxy.json", "generated-artifacts/IMStable.json", + "generated-artifacts/IMooniswap.json", "generated-artifacts/IUniswapExchange.json", "generated-artifacts/IUniswapExchangeFactory.json", "generated-artifacts/IUniswapV2Router01.json", @@ -37,6 +38,7 @@ "generated-artifacts/MixinAssetProxyDispatcher.json", "generated-artifacts/MixinAuthorizable.json", "generated-artifacts/MixinGasToken.json", + "generated-artifacts/MooniswapBridge.json", "generated-artifacts/MultiAssetProxy.json", "generated-artifacts/Ownable.json", "generated-artifacts/StaticCallProxy.json", @@ -78,6 +80,7 @@ "test/generated-artifacts/IGasToken.json", "test/generated-artifacts/IKyberNetworkProxy.json", "test/generated-artifacts/IMStable.json", + "test/generated-artifacts/IMooniswap.json", "test/generated-artifacts/IUniswapExchange.json", "test/generated-artifacts/IUniswapExchangeFactory.json", "test/generated-artifacts/IUniswapV2Router01.json", @@ -86,6 +89,7 @@ "test/generated-artifacts/MixinAssetProxyDispatcher.json", "test/generated-artifacts/MixinAuthorizable.json", "test/generated-artifacts/MixinGasToken.json", + "test/generated-artifacts/MooniswapBridge.json", "test/generated-artifacts/MultiAssetProxy.json", "test/generated-artifacts/Ownable.json", "test/generated-artifacts/StaticCallProxy.json", diff --git a/contracts/utils/contracts/src/DeploymentConstants.sol b/contracts/utils/contracts/src/DeploymentConstants.sol index 93749200e6..b90eb8698d 100644 --- a/contracts/utils/contracts/src/DeploymentConstants.sol +++ b/contracts/utils/contracts/src/DeploymentConstants.sol @@ -52,6 +52,8 @@ contract DeploymentConstants { 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; // // Ropsten addresses /////////////////////////////////////////////////////// // /// @dev Mainnet address of the WETH contract. @@ -272,4 +274,14 @@ contract DeploymentConstants { { return MUSD_ADDRESS; } + + /// @dev An overridable way to retrieve the Mooniswap registry address. + /// @return musd The Mooniswap registry address. + function _getMooniswapAddress() + internal + view + returns (address registry) + { + return MOONISWAP_REGISTRY; + } } diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 044d6138ba..7d14917a1f 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -57,6 +57,10 @@ { "note": "Merge `erc20-bridge-sampler` into this package", "pr": 2664 + }, + { + "note": "Added `Mooniswap`", + "pr": 2675 } ] }, diff --git a/packages/asset-swapper/contracts/src/CurveSampler.sol b/packages/asset-swapper/contracts/src/CurveSampler.sol index b5cce7c016..e31279817c 100644 --- a/packages/asset-swapper/contracts/src/CurveSampler.sol +++ b/packages/asset-swapper/contracts/src/CurveSampler.sol @@ -70,7 +70,9 @@ contract CurveSampler is uint256 buyAmount = 0; if (didSucceed) { buyAmount = abi.decode(resultData, (uint256)); - } else { + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { break; } makerTokenAmounts[i] = buyAmount; @@ -119,7 +121,9 @@ contract CurveSampler is uint256 sellAmount = 0; if (didSucceed) { sellAmount = abi.decode(resultData, (uint256)); - } else { + } + // Exit early if the amount is too high for the source to serve + if (sellAmount == 0) { break; } takerTokenAmounts[i] = sellAmount; diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index fa201baec8..0b634b9ba3 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -25,6 +25,7 @@ import "./KyberSampler.sol"; import "./LiquidityProviderSampler.sol"; import "./MultiBridgeSampler.sol"; import "./MStableSampler.sol"; +import "./MooniswapSampler.sol"; import "./NativeOrderSampler.sol"; import "./UniswapSampler.sol"; import "./UniswapV2Sampler.sol"; @@ -36,6 +37,7 @@ contract ERC20BridgeSampler is KyberSampler, LiquidityProviderSampler, MStableSampler, + MooniswapSampler, MultiBridgeSampler, NativeOrderSampler, UniswapSampler, diff --git a/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol b/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol index 13ca9075cf..977508be66 100644 --- a/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol +++ b/packages/asset-swapper/contracts/src/Eth2DaiSampler.sol @@ -61,46 +61,15 @@ contract Eth2DaiSampler is uint256 buyAmount = 0; if (didSucceed) { buyAmount = abi.decode(resultData, (uint256)); - } else{ + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { break; } makerTokenAmounts[i] = buyAmount; } } - /// @dev Sample sell quotes from Eth2Dai/Oasis using a hop to an intermediate token. - /// I.e WBTC/DAI via ETH or WBTC/ETH via DAI - /// @param takerToken Address of the taker token (what to sell). - /// @param makerToken Address of the maker token (what to buy). - /// @param intermediateToken Address of the token to hop to. - /// @param takerTokenAmounts Taker token sell amount for each sample. - /// @return makerTokenAmounts Maker amounts bought at each taker token - /// amount. - function sampleSellsFromEth2DaiHop( - address takerToken, - address makerToken, - address intermediateToken, - uint256[] memory takerTokenAmounts - ) - public - view - returns (uint256[] memory makerTokenAmounts) - { - if (makerToken == intermediateToken || takerToken == intermediateToken) { - return makerTokenAmounts; - } - uint256[] memory intermediateAmounts = sampleSellsFromEth2Dai( - takerToken, - intermediateToken, - takerTokenAmounts - ); - makerTokenAmounts = sampleSellsFromEth2Dai( - intermediateToken, - makerToken, - intermediateAmounts - ); - } - /// @dev Sample buy quotes from Eth2Dai/Oasis. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). @@ -131,7 +100,9 @@ contract Eth2DaiSampler is uint256 sellAmount = 0; if (didSucceed) { sellAmount = abi.decode(resultData, (uint256)); - } else { + } + // Exit early if the amount is too high for the source to serve + if (sellAmount == 0) { break; } takerTokenAmounts[i] = sellAmount; diff --git a/packages/asset-swapper/contracts/src/IMooniswap.sol b/packages/asset-swapper/contracts/src/IMooniswap.sol new file mode 100644 index 0000000000..bfec7c9871 --- /dev/null +++ b/packages/asset-swapper/contracts/src/IMooniswap.sol @@ -0,0 +1,37 @@ +/* + + Copyright 2020 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; + + +interface IMooniswapRegistry { + + function pools(address token1, address token2) external view returns(address); +} + +interface IMooniswap { + + function getReturn( + address fromToken, + address destToken, + uint256 amount + ) + external + view + returns(uint256 returnAmount); +} diff --git a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol b/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol index 2838787e70..3acbb17795 100644 --- a/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol +++ b/packages/asset-swapper/contracts/src/LiquidityProviderSampler.sol @@ -77,8 +77,9 @@ contract LiquidityProviderSampler is uint256 buyAmount = 0; if (didSucceed) { buyAmount = abi.decode(resultData, (uint256)); - } else { - // Exit early if the amount is too high for the liquidity provider to serve + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { break; } makerTokenAmounts[i] = buyAmount; diff --git a/packages/asset-swapper/contracts/src/MStableSampler.sol b/packages/asset-swapper/contracts/src/MStableSampler.sol index ae6c4767e5..fa468824a6 100644 --- a/packages/asset-swapper/contracts/src/MStableSampler.sol +++ b/packages/asset-swapper/contracts/src/MStableSampler.sol @@ -65,7 +65,9 @@ contract MStableSampler is uint256 buyAmount = 0; if (didSucceed) { (, , buyAmount) = abi.decode(resultData, (bool, string, uint256)); - } else { + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { break; } makerTokenAmounts[i] = buyAmount; diff --git a/packages/asset-swapper/contracts/src/MooniswapSampler.sol b/packages/asset-swapper/contracts/src/MooniswapSampler.sol new file mode 100644 index 0000000000..2e2dfc2edb --- /dev/null +++ b/packages/asset-swapper/contracts/src/MooniswapSampler.sol @@ -0,0 +1,146 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; +import "./IMooniswap.sol"; +import "./ApproximateBuys.sol"; +import "./SamplerUtils.sol"; + + +contract MooniswapSampler is + DeploymentConstants, + SamplerUtils, + ApproximateBuys +{ + /// @dev Gas limit for Mooniswap calls. + uint256 constant private MOONISWAP_CALL_GAS = 150e3; // 150k + + /// @dev Sample sell quotes from Mooniswap. + /// @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 sampleSellsFromMooniswap( + address takerToken, + address makerToken, + uint256[] memory takerTokenAmounts + ) + public + view + returns (uint256[] memory makerTokenAmounts) + { + _assertValidPair(makerToken, takerToken); + uint256 numSamples = takerTokenAmounts.length; + makerTokenAmounts = new uint256[](numSamples); + + address _takerToken = takerToken == _getWethAddress() ? address(0) : takerToken; + address _makerToken = makerToken == _getWethAddress() ? address(0) : makerToken; + // Find the pool for the pair, ETH is represented + // as address(0) + IMooniswap pool = IMooniswap( + IMooniswapRegistry(_getMooniswapAddress()).pools(_takerToken, _makerToken) + ); + // If there is no pool then return early + if (address(pool) == address(0)) { + return makerTokenAmounts; + } + + uint256 poolBalance = _takerToken == address(0) ? address(pool).balance : IERC20Token(_takerToken).balanceOf(address(pool)); + + for (uint256 i = 0; i < numSamples; i++) { + // If the pool balance is smaller than the sell amount + // don't sample to avoid multiplication overflow in buys + if (poolBalance < takerTokenAmounts[i]) { + break; + } + (bool didSucceed, bytes memory resultData) = + address(pool).staticcall.gas(MOONISWAP_CALL_GAS)( + abi.encodeWithSelector( + IMooniswap(0).getReturn.selector, + _takerToken, + _makerToken, + takerTokenAmounts[i] + )); + uint256 buyAmount = 0; + if (didSucceed) { + buyAmount = abi.decode(resultData, (uint256)); + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { + break; + } + makerTokenAmounts[i] = buyAmount; + } + } + + /// @dev Sample buy quotes from Mooniswap. + /// @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. + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromMooniswap( + address takerToken, + address makerToken, + uint256[] memory makerTokenAmounts + ) + public + view + returns (uint256[] memory takerTokenAmounts) + { + return _sampleApproximateBuys( + ApproximateBuyQuoteOpts({ + makerTokenData: abi.encode(makerToken), + takerTokenData: abi.encode(takerToken), + getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap + }), + makerTokenAmounts + ); + } + + function _sampleSellForApproximateBuyFromMooniswap( + bytes memory takerTokenData, + bytes memory makerTokenData, + uint256 sellAmount + ) + private + view + returns (uint256 buyAmount) + { + (address takerToken) = + abi.decode(takerTokenData, (address)); + (address makerToken) = + abi.decode(makerTokenData, (address)); + (bool success, bytes memory resultData) = + address(this).staticcall(abi.encodeWithSelector( + this.sampleSellsFromMooniswap.selector, + takerToken, + makerToken, + _toSingleValueArray(sellAmount) + )); + if (!success) { + return 0; + } + // solhint-disable-next-line indent + return abi.decode(resultData, (uint256[]))[0]; + } +} diff --git a/packages/asset-swapper/contracts/src/MultiBridgeSampler.sol b/packages/asset-swapper/contracts/src/MultiBridgeSampler.sol index 0e85f34f0c..59ca4a7c98 100644 --- a/packages/asset-swapper/contracts/src/MultiBridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/MultiBridgeSampler.sol @@ -69,10 +69,12 @@ contract MultiBridgeSampler { uint256 buyAmount = 0; if (didSucceed) { buyAmount = abi.decode(resultData, (uint256)); - } else { - // Exit early if the amount is too high for the liquidity provider to serve + } + // Exit early if the amount is too high for the source to serve + if (buyAmount == 0) { break; } + makerTokenAmounts[i] = buyAmount; } } diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 228de61dfc..39949fe3c9 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -10,6 +10,7 @@ "scripts": { "build": "yarn pre_build && tsc -b", "watch": "tsc -w -p tsconfig.json", + "watch:contracts": "sol-compiler -w", "build:ci": "yarn build", "pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy", "compile": "sol-compiler", @@ -36,7 +37,7 @@ "config": { "publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ApproximateBuys|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|ICurve|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|UniswapSampler|UniswapV2Sampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|ICurve|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|UniswapSampler|UniswapV2Sampler).json", "postpublish": { "assets": [] } diff --git a/packages/asset-swapper/src/swap_quoter.ts b/packages/asset-swapper/src/swap_quoter.ts index fbe96ed95c..03c359fdd3 100644 --- a/packages/asset-swapper/src/swap_quoter.ts +++ b/packages/asset-swapper/src/swap_quoter.ts @@ -2,7 +2,7 @@ import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/cont import { DevUtilsContract } from '@0x/contract-wrappers'; import { schemas } from '@0x/json-schemas'; import { assetDataUtils, SignedOrder } from '@0x/order-utils'; -import { MeshOrderProviderOpts, Orderbook, SRAPollingOrderProviderOpts } from '@0x/orderbook'; +import { APIOrder, MeshOrderProviderOpts, Orderbook, SRAPollingOrderProviderOpts } from '@0x/orderbook'; import { BigNumber, providerUtils } from '@0x/utils'; import { BlockParamLiteral, SupportedProvider, ZeroExProvider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -422,7 +422,7 @@ export class SwapQuoter { const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress); let [sellOrders, buyOrders] = options.excludedSources && options.excludedSources.includes(ERC20BridgeSource.Native) - ? Promise.resolve([[], []]) + ? await Promise.resolve([[] as APIOrder[], [] as APIOrder[]]) : await Promise.all([ this.orderbook.getOrdersAsync(makerAssetData, takerAssetData), this.orderbook.getOrdersAsync(takerAssetData, makerAssetData), 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 12350564dc..c7f3e3186a 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -16,6 +16,7 @@ export const SELL_SOURCES = [ ERC20BridgeSource.Balancer, // ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports batch requests ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, ]; /** @@ -30,6 +31,7 @@ export const BUY_SOURCES = [ ERC20BridgeSource.Balancer, // ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports buy quotes ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, ]; export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { @@ -49,12 +51,7 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { /** * Sources to poll for ETH fee price estimates. */ -export const FEE_QUOTE_SOURCES = [ - ERC20BridgeSource.Uniswap, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Kyber, -]; +export const FEE_QUOTE_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2]; /** * Mainnet Curve configuration 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 fbbd192fcc..01dee6ca1e 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -106,7 +106,7 @@ export class MarketOperationUtils { ), // Get ETH -> maker token price. await DexOrderSampler.ops.getMedianSellRateAsync( - difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), + difference(FEE_QUOTE_SOURCES, _opts.excludedSources), makerToken, this._wethAddress, ONE_ETHER, @@ -118,7 +118,7 @@ export class MarketOperationUtils { ), // Get ETH -> taker token price. await DexOrderSampler.ops.getMedianSellRateAsync( - difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), + difference(FEE_QUOTE_SOURCES, _opts.excludedSources), takerToken, this._wethAddress, ONE_ETHER, @@ -225,7 +225,7 @@ export class MarketOperationUtils { ), // Get ETH -> maker token price. await DexOrderSampler.ops.getMedianSellRateAsync( - difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), + difference(FEE_QUOTE_SOURCES, _opts.excludedSources), makerToken, this._wethAddress, ONE_ETHER, @@ -236,7 +236,7 @@ export class MarketOperationUtils { ), // Get ETH -> taker token price. await DexOrderSampler.ops.getMedianSellRateAsync( - difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), + difference(FEE_QUOTE_SOURCES, _opts.excludedSources), takerToken, this._wethAddress, ONE_ETHER, 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 446cd11387..5d4b9ba5c4 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -201,6 +201,8 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath return (fill.fillData as MultiBridgeFillData).poolAddress; case ERC20BridgeSource.MStable: return opts.contractAddresses.mStableBridge; + case ERC20BridgeSource.Mooniswap: + return opts.contractAddresses.mooniswapBridge; default: break; } 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 8813327fb7..74212dd254 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 @@ -365,7 +365,7 @@ export const samplerOperations = { source: ERC20BridgeSource.MStable, encodeCall: contract => { return contract - .sampleSellsFromMStable(makerToken, takerToken, takerFillAmounts) + .sampleSellsFromMStable(takerToken, makerToken, takerFillAmounts) .getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { @@ -380,7 +380,7 @@ export const samplerOperations = { source: ERC20BridgeSource.MStable, encodeCall: contract => { return contract - .sampleBuysFromMStable(makerToken, takerToken, makerFillAmounts) + .sampleBuysFromMStable(takerToken, makerToken, makerFillAmounts) .getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { @@ -390,6 +390,40 @@ export const samplerOperations = { }, }; }, + getMooniswapSellQuotes( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return { + source: ERC20BridgeSource.Mooniswap, + encodeCall: contract => { + return contract + .sampleSellsFromMooniswap(takerToken, makerToken, takerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract + .getABIDecodedReturnData('sampleSellsFromMooniswap', callResults) + .map(amount => ({ amount })); + }, + }; + }, + getMooniswapBuyQuotes(makerToken: string, takerToken: string, makerFillAmounts: BigNumber[]): SourceQuoteOperation { + return { + source: ERC20BridgeSource.Mooniswap, + encodeCall: contract => { + return contract + .sampleBuysFromMooniswap(takerToken, makerToken, makerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract + .getABIDecodedReturnData('sampleBuysFromMooniswap', callResults) + .map(amount => ({ amount })); + }, + }; + }, getMedianSellRateAsync: async ( sources: ERC20BridgeSource[], makerToken: string, @@ -570,6 +604,12 @@ export const samplerOperations = { ); case ERC20BridgeSource.MStable: return samplerOperations.getMStableSellQuotes(makerToken, takerToken, takerFillAmounts); + case ERC20BridgeSource.Mooniswap: + return samplerOperations.getMooniswapSellQuotes( + makerToken, + takerToken, + takerFillAmounts, + ); default: throw new Error(`Unsupported sell sample source: ${source}`); } @@ -690,6 +730,12 @@ export const samplerOperations = { return []; // FIXME: Waiting for Bancor SDK to support buy quotes, but don't throw an error here case ERC20BridgeSource.MStable: return samplerOperations.getMStableBuyQuotes(makerToken, takerToken, makerFillAmounts); + case ERC20BridgeSource.Mooniswap: + return samplerOperations.getMooniswapBuyQuotes( + makerToken, + takerToken, + makerFillAmounts, + ); default: throw new Error(`Unsupported buy sample source: ${source}`); } 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 0856813fed..7db7c5514f 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -40,6 +40,7 @@ export enum ERC20BridgeSource { Balancer = 'Balancer', Bancor = 'Bancor', MStable = 'mStable', + Mooniswap = 'Mooniswap', } // tslint:disable: enum-naming diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index 3d1c893c7b..7e29c82804 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -19,12 +19,14 @@ import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkPr import * as IKyberStorage from '../test/generated-artifacts/IKyberStorage.json'; import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json'; import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json'; +import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; import * as IMStable from '../test/generated-artifacts/IMStable.json'; import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json'; import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json'; import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json'; import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json'; import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.json'; +import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json'; import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json'; import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json'; import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json'; @@ -49,12 +51,14 @@ export const artifacts = { ILiquidityProvider: ILiquidityProvider as ContractArtifact, ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact, IMStable: IMStable as ContractArtifact, + IMooniswap: IMooniswap as ContractArtifact, IMultiBridge: IMultiBridge as ContractArtifact, IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact, IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, KyberSampler: KyberSampler as ContractArtifact, LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact, MStableSampler: MStableSampler as ContractArtifact, + MooniswapSampler: MooniswapSampler as ContractArtifact, MultiBridgeSampler: MultiBridgeSampler as ContractArtifact, NativeOrderSampler: NativeOrderSampler as ContractArtifact, SamplerUtils: SamplerUtils 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 7ed3d87255..7d23096ce9 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -86,6 +86,8 @@ describe('MarketOperationUtils tests', () => { return ERC20BridgeSource.Curve; case contractAddresses.mStableBridge.toLowerCase(): return ERC20BridgeSource.MStable; + case contractAddresses.mooniswapBridge.toLowerCase(): + return ERC20BridgeSource.Mooniswap; default: break; } @@ -293,6 +295,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0), }; interface FillDataBySource { @@ -430,6 +433,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Curve, ERC20BridgeSource.Balancer, ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, ], allowFallback: false, shouldBatchBridgeOrders: false, @@ -842,6 +846,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Curve, ERC20BridgeSource.Balancer, ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, ], allowFallback: false, shouldBatchBridgeOrders: false, diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index cb27839ae7..a17bb121d7 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -18,12 +18,14 @@ export * from '../test/generated-wrappers/i_kyber_storage'; export * from '../test/generated-wrappers/i_liquidity_provider'; export * from '../test/generated-wrappers/i_liquidity_provider_registry'; export * from '../test/generated-wrappers/i_m_stable'; +export * from '../test/generated-wrappers/i_mooniswap'; export * from '../test/generated-wrappers/i_multi_bridge'; export * from '../test/generated-wrappers/i_uniswap_exchange_quotes'; export * from '../test/generated-wrappers/i_uniswap_v2_router01'; export * from '../test/generated-wrappers/kyber_sampler'; export * from '../test/generated-wrappers/liquidity_provider_sampler'; export * from '../test/generated-wrappers/m_stable_sampler'; +export * from '../test/generated-wrappers/mooniswap_sampler'; export * from '../test/generated-wrappers/multi_bridge_sampler'; export * from '../test/generated-wrappers/native_order_sampler'; export * from '../test/generated-wrappers/sampler_utils'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json index cb1f20d502..852a297f6d 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -23,12 +23,14 @@ "test/generated-artifacts/ILiquidityProvider.json", "test/generated-artifacts/ILiquidityProviderRegistry.json", "test/generated-artifacts/IMStable.json", + "test/generated-artifacts/IMooniswap.json", "test/generated-artifacts/IMultiBridge.json", "test/generated-artifacts/IUniswapExchangeQuotes.json", "test/generated-artifacts/IUniswapV2Router01.json", "test/generated-artifacts/KyberSampler.json", "test/generated-artifacts/LiquidityProviderSampler.json", "test/generated-artifacts/MStableSampler.json", + "test/generated-artifacts/MooniswapSampler.json", "test/generated-artifacts/MultiBridgeSampler.json", "test/generated-artifacts/NativeOrderSampler.json", "test/generated-artifacts/SamplerUtils.json", diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index ee3c6944aa..b13fd37cef 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -29,6 +29,10 @@ { "note": "Deploy `MStableBridge` on Mainnet", "pr": 2662 + }, + { + "note": "Deploy `MooniswapBridge` on Mainnet", + "pr": 2675 } ] }, diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 621afcfa12..d60b0aa07b 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -41,6 +41,7 @@ "exchangeProxyTransformerDeployer": "0x80a36559ab9a497fb658325ed771a584eb0f13da", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3", + "mooniswapBridge": "0xf384663c04a77a732e7a222c65b33fe471c7011c", "transformers": { "wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8", "payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841", @@ -90,6 +91,7 @@ "exchangeProxyTransformerDeployer": "0x80a36559ab9a497fb658325ed771a584eb0f13da", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", + "mooniswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8", "payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841", @@ -139,6 +141,7 @@ "exchangeProxyTransformerDeployer": "0x80a36559ab9a497fb658325ed771a584eb0f13da", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", + "mooniswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8", "payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841", @@ -188,6 +191,7 @@ "exchangeProxyTransformerDeployer": "0x80a36559ab9a497fb658325ed771a584eb0f13da", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", + "mooniswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x7bab5f7299e1ca123bb44eb71e6c89be7e558cc8", "payTakerTransformer": "0xe8c07a119452b55eee2f999478aab97f3656d841", @@ -237,6 +241,7 @@ "exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631", "exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa", "mStableBridge": "0x0000000000000000000000000000000000000000", + "mooniswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a", "payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 68e99dc4d4..005134b745 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -42,6 +42,7 @@ export interface ContractAddresses { exchangeProxyTransformerDeployer: string; exchangeProxyFlashWallet: string; mStableBridge: string; + mooniswapBridge: string; transformers: { wethTransformer: string; payTakerTransformer: string; diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index 9d7014266f..30b4734b16 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -408,6 +408,7 @@ export async function runMigrationsAsync( bancorBridge: NULL_ADDRESS, exchangeProxyGovernor: NULL_ADDRESS, mStableBridge: NULL_ADDRESS, + mooniswapBridge: NULL_ADDRESS, exchangeProxy: exchangeProxy.address, exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, exchangeProxyTransformerDeployer: txDefaults.from,