From 630108ccb6fe4ff411e48e2b8044260caf9c42f0 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 9 Sep 2020 21:35:24 +1000 Subject: [PATCH] feat: Swerve Finance and SushiSwap (#2698) * feat: Swerve Finance * export SwerveFillData * test and CHANGELOG * feat: Sushiswap (#2700) * feat: SushiSwap * Changelog * fix tests * Deployed SushiSwap bridge * Fix test * IGNORED_EXCESSIVE_TYPES --- .../contracts/src/bridges/SushiSwapBridge.sol | 136 ++++++++++++++++++ contracts/asset-proxy/package.json | 2 +- contracts/asset-proxy/src/artifacts.ts | 2 + contracts/asset-proxy/src/wrappers.ts | 1 + contracts/asset-proxy/test/artifacts.ts | 2 + contracts/asset-proxy/test/wrappers.ts | 1 + contracts/asset-proxy/tsconfig.json | 2 + .../contracts/src/DeploymentConstants.sol | 4 +- packages/asset-swapper/CHANGELOG.json | 8 ++ .../contracts/src/ERC20BridgeSampler.sol | 4 +- .../contracts/src/SushiSwapSampler.sol | 103 +++++++++++++ packages/asset-swapper/package.json | 2 +- packages/asset-swapper/src/index.ts | 4 + .../utils/market_operation_utils/constants.ts | 20 +++ .../market_operation_utils/curve_utils.ts | 8 +- .../utils/market_operation_utils/orders.ts | 47 ++++-- .../sampler_operations.ts | 126 +++++++++++++++- .../src/utils/market_operation_utils/types.ts | 15 ++ packages/asset-swapper/test/artifacts.ts | 2 + .../test/market_operation_utils_test.ts | 73 ++++++---- packages/asset-swapper/test/wrappers.ts | 1 + packages/asset-swapper/tsconfig.json | 1 + packages/contract-addresses/addresses.json | 5 + packages/contract-addresses/src/index.ts | 1 + packages/migrations/src/migration.ts | 1 + .../monorepo-scripts/src/doc_gen_configs.ts | 1 + 26 files changed, 527 insertions(+), 45 deletions(-) create mode 100644 contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol create mode 100644 packages/asset-swapper/contracts/src/SushiSwapSampler.sol diff --git a/contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol b/contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol new file mode 100644 index 0000000000..480942d9c3 --- /dev/null +++ b/contracts/asset-proxy/contracts/src/bridges/SushiSwapBridge.sol @@ -0,0 +1,136 @@ +/* + + 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/LibAddressArray.sol"; +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; +import "../interfaces/IUniswapV2Router01.sol"; +import "../interfaces/IERC20Bridge.sol"; + + +// solhint-disable space-after-comma +// solhint-disable not-rely-on-time +contract SushiSwapBridge is + IERC20Bridge, + IWallet, + DeploymentConstants +{ + struct TransferState { + address[] path; + address router; + uint256 fromTokenBalance; + } + + /// @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) + { + // hold variables to get around stack depth limitations + TransferState memory state; + + // Decode the bridge data to get the `fromTokenAddress`. + // solhint-disable indent + (state.path, state.router) = abi.decode(bridgeData, (address[], address)); + // solhint-enable indent + + require(state.path.length >= 2, "SushiSwapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO"); + require(state.path[state.path.length - 1] == toTokenAddress, "SushiSwapBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"); + + // Just transfer the tokens if they're the same. + if (state.path[0] == toTokenAddress) { + LibERC20Token.transfer(state.path[0], to, amount); + return BRIDGE_SUCCESS; + } + + // Get our balance of `fromTokenAddress` token. + state.fromTokenBalance = IERC20Token(state.path[0]).balanceOf(address(this)); + + // Grant the SushiSwap router an allowance. + LibERC20Token.approveIfBelow( + state.path[0], + state.router, + state.fromTokenBalance + ); + + // Buy as much `toTokenAddress` token with `fromTokenAddress` token + // and transfer it to `to`. + IUniswapV2Router01 router = IUniswapV2Router01(state.router); + uint[] memory amounts = router.swapExactTokensForTokens( + // Sell all tokens we hold. + state.fromTokenBalance, + // Minimum buy amount. + amount, + // Convert `fromTokenAddress` to `toTokenAddress`. + state.path, + // Recipient is `to`. + to, + // Expires after this block. + block.timestamp + ); + + emit ERC20BridgeTransfer( + // input token + state.path[0], + // output token + toTokenAddress, + // input token amount + state.fromTokenBalance, + // output token amount + amounts[amounts.length - 1], + 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/package.json b/contracts/asset-proxy/package.json index afb53323b9..cf4e4f3222 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|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": "./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|SushiSwapBridge|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 52c80962c5..5385d5d000 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -44,6 +44,7 @@ import * as MStableBridge from '../generated-artifacts/MStableBridge.json'; import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json'; import * as Ownable from '../generated-artifacts/Ownable.json'; import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json'; +import * as SushiSwapBridge from '../generated-artifacts/SushiSwapBridge.json'; import * as TestBancorBridge from '../generated-artifacts/TestBancorBridge.json'; import * as TestChaiBridge from '../generated-artifacts/TestChaiBridge.json'; import * as TestDexForwarderBridge from '../generated-artifacts/TestDexForwarderBridge.json'; @@ -77,6 +78,7 @@ export const artifacts = { MStableBridge: MStableBridge as ContractArtifact, MixinGasToken: MixinGasToken as ContractArtifact, MooniswapBridge: MooniswapBridge as ContractArtifact, + SushiSwapBridge: SushiSwapBridge as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact, UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, IAssetData: IAssetData as ContractArtifact, diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index caa4d97881..8107eaf766 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -42,6 +42,7 @@ 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'; +export * from '../generated-wrappers/sushi_swap_bridge'; export * from '../generated-wrappers/test_bancor_bridge'; export * from '../generated-wrappers/test_chai_bridge'; export * from '../generated-wrappers/test_dex_forwarder_bridge'; diff --git a/contracts/asset-proxy/test/artifacts.ts b/contracts/asset-proxy/test/artifacts.ts index f932ede72f..aa01f1dc52 100644 --- a/contracts/asset-proxy/test/artifacts.ts +++ b/contracts/asset-proxy/test/artifacts.ts @@ -44,6 +44,7 @@ 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'; import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json'; +import * as SushiSwapBridge from '../test/generated-artifacts/SushiSwapBridge.json'; import * as TestBancorBridge from '../test/generated-artifacts/TestBancorBridge.json'; import * as TestChaiBridge from '../test/generated-artifacts/TestChaiBridge.json'; import * as TestDexForwarderBridge from '../test/generated-artifacts/TestDexForwarderBridge.json'; @@ -77,6 +78,7 @@ export const artifacts = { MStableBridge: MStableBridge as ContractArtifact, MixinGasToken: MixinGasToken as ContractArtifact, MooniswapBridge: MooniswapBridge as ContractArtifact, + SushiSwapBridge: SushiSwapBridge as ContractArtifact, UniswapBridge: UniswapBridge as ContractArtifact, UniswapV2Bridge: UniswapV2Bridge as ContractArtifact, IAssetData: IAssetData as ContractArtifact, diff --git a/contracts/asset-proxy/test/wrappers.ts b/contracts/asset-proxy/test/wrappers.ts index d1328370d8..4ae6089533 100644 --- a/contracts/asset-proxy/test/wrappers.ts +++ b/contracts/asset-proxy/test/wrappers.ts @@ -42,6 +42,7 @@ 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'; +export * from '../test/generated-wrappers/sushi_swap_bridge'; export * from '../test/generated-wrappers/test_bancor_bridge'; export * from '../test/generated-wrappers/test_chai_bridge'; export * from '../test/generated-wrappers/test_dex_forwarder_bridge'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index a8072a6556..ccb4c12320 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -42,6 +42,7 @@ "generated-artifacts/MultiAssetProxy.json", "generated-artifacts/Ownable.json", "generated-artifacts/StaticCallProxy.json", + "generated-artifacts/SushiSwapBridge.json", "generated-artifacts/TestBancorBridge.json", "generated-artifacts/TestChaiBridge.json", "generated-artifacts/TestDexForwarderBridge.json", @@ -93,6 +94,7 @@ "test/generated-artifacts/MultiAssetProxy.json", "test/generated-artifacts/Ownable.json", "test/generated-artifacts/StaticCallProxy.json", + "test/generated-artifacts/SushiSwapBridge.json", "test/generated-artifacts/TestBancorBridge.json", "test/generated-artifacts/TestChaiBridge.json", "test/generated-artifacts/TestDexForwarderBridge.json", diff --git a/contracts/utils/contracts/src/DeploymentConstants.sol b/contracts/utils/contracts/src/DeploymentConstants.sol index f85cfc7dc6..9993d27dd5 100644 --- a/contracts/utils/contracts/src/DeploymentConstants.sol +++ b/contracts/utils/contracts/src/DeploymentConstants.sol @@ -288,11 +288,11 @@ contract DeploymentConstants { } /// @dev An overridable way to retrieve the Mooniswap registry address. - /// @return musd The Mooniswap registry address. + /// @return registry The Mooniswap registry address. function _getMooniswapAddress() internal view - returns (address registry) + returns (address) { return MOONISWAP_REGISTRY; } diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index d09eb21524..df4b66f6ab 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -101,6 +101,14 @@ { "note": "Return Mooniswap pool in sampler and encode it in bridge data", "pr": 2692 + }, + { + "note": "Added `Swerve`", + "pr": 2698 + }, + { + "note": "Added `SushiSwap`", + "pr": 2698 } ] }, diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index 6e75013052..db844350e0 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -28,9 +28,10 @@ import "./MultiBridgeSampler.sol"; import "./MStableSampler.sol"; import "./MooniswapSampler.sol"; import "./NativeOrderSampler.sol"; +import "./SushiSwapSampler.sol"; +import "./TwoHopSampler.sol"; import "./UniswapSampler.sol"; import "./UniswapV2Sampler.sol"; -import "./TwoHopSampler.sol"; contract ERC20BridgeSampler is @@ -43,6 +44,7 @@ contract ERC20BridgeSampler is MooniswapSampler, MultiBridgeSampler, NativeOrderSampler, + SushiSwapSampler, TwoHopSampler, UniswapSampler, UniswapV2Sampler diff --git a/packages/asset-swapper/contracts/src/SushiSwapSampler.sol b/packages/asset-swapper/contracts/src/SushiSwapSampler.sol new file mode 100644 index 0000000000..1239d2a579 --- /dev/null +++ b/packages/asset-swapper/contracts/src/SushiSwapSampler.sol @@ -0,0 +1,103 @@ +/* + + 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 "./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++) { + (bool didSucceed, bytes memory resultData) = + router.staticcall.gas(SUSHISWAP_CALL_GAS)( + abi.encodeWithSelector( + IUniswapV2Router01(0).getAmountsOut.selector, + takerTokenAmounts[i], + path + )); + uint256 buyAmount = 0; + if (didSucceed) { + // solhint-disable-next-line indent + buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1]; + } else { + break; + } + makerTokenAmounts[i] = buyAmount; + } + } + + /// @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++) { + (bool didSucceed, bytes memory resultData) = + router.staticcall.gas(SUSHISWAP_CALL_GAS)( + abi.encodeWithSelector( + IUniswapV2Router01(0).getAmountsIn.selector, + makerTokenAmounts[i], + path + )); + uint256 sellAmount = 0; + if (didSucceed) { + // solhint-disable-next-line indent + sellAmount = abi.decode(resultData, (uint256[]))[0]; + } else { + break; + } + takerTokenAmounts[i] = sellAmount; + } + } +} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index c2826d5ecf..9674e291e0 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -38,7 +38,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|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json", "postpublish": { "assets": [] } diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index fbf49b2185..96bf1d4073 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -99,6 +99,7 @@ export { MarketOperation, MarketSellSwapQuote, MockedRfqtFirmQuoteResponse, + OrderPrunerPermittedFeeTypes, RfqtMakerAssetOfferings, RfqtRequestOpts, SamplerOverrides, @@ -148,6 +149,9 @@ export { OptimizedMarketOrder, SourceInfo, SourceQuoteOperation, + SushiSwapFillData, + SwerveFillData, + SwerveInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './utils/market_operation_utils/types'; 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 9cbeefeb70..30d46a49a4 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -17,6 +17,8 @@ export const SELL_SOURCES = [ // ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports batch requests ERC20BridgeSource.MStable, ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.Swerve, + ERC20BridgeSource.SushiSwap, ]; /** @@ -32,6 +34,8 @@ export const BUY_SOURCES = [ // ERC20BridgeSource.Bancor, // FIXME: Disabled until Bancor SDK supports buy quotes ERC20BridgeSource.MStable, ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.Swerve, + ERC20BridgeSource.SushiSwap, ]; export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { @@ -132,6 +136,20 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = { ], }, }; +export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = { + swUSD: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, + poolAddress: '0x329239599afB305DA0A2eC69c58F8a6697F9F88d', + tokens: [ + '0x6b175474e89094c44da98b954eedeac495271d0f', + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + '0xdac17f958d2ee523a2206206994597c13d831ec7', + '0x0000000000085d4780b73119b644ae5ecd22b376', + ], + }, +}; export const MAINNET_KYBER_RESERVE_IDS: { [name: string]: string } = { Reserve1: '0xff4b796265722046707200000000000000000000000000000000000000000000', @@ -169,6 +187,8 @@ export const MAINNET_KYBER_TOKEN_RESERVE_IDS: { [token: string]: string } = { '0xaa42414e44000000000000000000000000000000000000000000000000000000', }; +export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'; + export const ERC20_PROXY_ID = '0xf47261b0'; export const WALLET_SIGNATURE = '0x04'; export const ONE_ETHER = new BigNumber(1e18); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts index 182029e9fb..e1b70ab492 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts @@ -1,7 +1,11 @@ -import { MAINNET_CURVE_INFOS } from './constants'; -import { CurveInfo } from './types'; +import { MAINNET_CURVE_INFOS, MAINNET_SWERVE_INFOS } from './constants'; +import { CurveInfo, SwerveInfo } from './types'; // tslint:disable completed-docs export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] { return Object.values(MAINNET_CURVE_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))); } + +export function getSwerveInfosForPair(takerToken: string, makerToken: string): SwerveInfo[] { + return Object.values(MAINNET_SWERVE_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))); +} 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 76f3ee01f5..efcb699a7a 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -35,6 +35,8 @@ import { NativeCollapsedFill, OptimizedMarketOrder, OrderDomain, + SushiSwapFillData, + SwerveFillData, UniswapV2FillData, } from './types'; @@ -222,8 +224,12 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath return opts.contractAddresses.uniswapBridge; case ERC20BridgeSource.UniswapV2: return opts.contractAddresses.uniswapV2Bridge; + case ERC20BridgeSource.SushiSwap: + return opts.contractAddresses.sushiswapBridge; case ERC20BridgeSource.Curve: return opts.contractAddresses.curveBridge; + case ERC20BridgeSource.Swerve: + return opts.contractAddresses.curveBridge; case ERC20BridgeSource.Bancor: return opts.contractAddresses.bancorBridge; case ERC20BridgeSource.Balancer: @@ -266,6 +272,20 @@ function createBridgeOrder( ), ); break; + case ERC20BridgeSource.Swerve: + const swerveFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createCurveBridgeData( + swerveFillData.pool.poolAddress, + swerveFillData.pool.exchangeFunctionSelector, + takerToken, + swerveFillData.fromTokenIdx, + swerveFillData.toTokenIdx, + ), + ); + break; case ERC20BridgeSource.Balancer: const balancerFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( @@ -290,6 +310,14 @@ function createBridgeOrder( createUniswapV2BridgeData(uniswapV2FillData.tokenAddressPath), ); break; + case ERC20BridgeSource.SushiSwap: + const sushiSwapFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createSushiSwapBridgeData(sushiSwapFillData.tokenAddressPath, sushiSwapFillData.router), + ); + break; case ERC20BridgeSource.MultiBridge: makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerToken, @@ -431,25 +459,24 @@ function createCurveBridgeData( fromTokenIdx: number, toTokenIdx: number, ): string { - const curveBridgeDataEncoder = AbiEncoder.create([ + const encoder = AbiEncoder.create([ { name: 'curveAddress', type: 'address' }, { name: 'exchangeFunctionSelector', type: 'bytes4' }, { name: 'fromTokenAddress', type: 'address' }, { name: 'fromTokenIdx', type: 'int128' }, { name: 'toTokenIdx', type: 'int128' }, ]); - return curveBridgeDataEncoder.encode([ - curveAddress, - exchangeFunctionSelector, - takerToken, - fromTokenIdx, - toTokenIdx, - ]); + return encoder.encode([curveAddress, exchangeFunctionSelector, takerToken, fromTokenIdx, toTokenIdx]); } function createUniswapV2BridgeData(tokenAddressPath: string[]): string { - const uniswapV2BridgeDataEncoder = AbiEncoder.create('(address[])'); - return uniswapV2BridgeDataEncoder.encode([tokenAddressPath]); + const encoder = AbiEncoder.create('(address[])'); + return encoder.encode([tokenAddressPath]); +} + +function createSushiSwapBridgeData(tokenAddressPath: string[], router: string): string { + const encoder = AbiEncoder.create('(address[],address)'); + return encoder.encode([tokenAddressPath, router]); } function getSlippedBridgeAssetAmounts(fill: CollapsedFill, opts: CreateOrderFromPathOpts): [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 a67d3dce66..20ca968cf6 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 @@ -6,8 +6,8 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils'; import { BancorService } from './bancor_service'; -import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; -import { getCurveInfosForPair } from './curve_utils'; +import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; +import { getCurveInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getKyberReserveIdsForPair } from './kyber_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; import { getIntermediateTokens } from './multihop_utils'; @@ -27,6 +27,9 @@ import { MultiBridgeFillData, MultiHopFillData, SourceQuoteOperation, + SushiSwapFillData, + SwerveFillData, + SwerveInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './types'; @@ -315,6 +318,62 @@ export class SamplerOperations { }); } + 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 getBalancerSellQuotes( poolAddress: string, makerToken: string, @@ -621,6 +680,32 @@ 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 getMedianSellRate( sources: ERC20BridgeSource[], makerToken: string, @@ -784,6 +869,17 @@ export class SamplerOperations { ); } return ops; + case ERC20BridgeSource.SushiSwap: + const sushiOps = [this.getSushiSwapSellQuotes([takerToken, makerToken], takerFillAmounts)]; + if (takerToken !== wethAddress && makerToken !== wethAddress) { + sushiOps.push( + this.getSushiSwapSellQuotes( + [takerToken, wethAddress, makerToken], + takerFillAmounts, + ), + ); + } + return sushiOps; case ERC20BridgeSource.Kyber: return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId => this.getKyberSellQuotes(reserveId, makerToken, takerToken, takerFillAmounts), @@ -797,6 +893,15 @@ export class SamplerOperations { takerFillAmounts, ), ); + case ERC20BridgeSource.Swerve: + return getSwerveInfosForPair(takerToken, makerToken).map(pool => + this.getSwerveSellQuotes( + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), + takerFillAmounts, + ), + ); case ERC20BridgeSource.LiquidityProvider: if (liquidityProviderRegistryAddress === undefined) { throw new Error( @@ -865,6 +970,14 @@ export class SamplerOperations { ); } return ops; + case ERC20BridgeSource.SushiSwap: + const sushiOps = [this.getSushiSwapBuyQuotes([takerToken, makerToken], makerFillAmounts)]; + if (takerToken !== wethAddress && makerToken !== wethAddress) { + sushiOps.push( + this.getSushiSwapBuyQuotes([takerToken, wethAddress, makerToken], makerFillAmounts), + ); + } + return sushiOps; case ERC20BridgeSource.Kyber: return getKyberReserveIdsForPair(takerToken, makerToken).map(reserveId => this.getKyberBuyQuotes(reserveId, makerToken, takerToken, makerFillAmounts), @@ -878,6 +991,15 @@ export class SamplerOperations { makerFillAmounts, ), ); + case ERC20BridgeSource.Swerve: + return getSwerveInfosForPair(takerToken, makerToken).map(pool => + this.getSwerveBuyQuotes( + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), + makerFillAmounts, + ), + ); case ERC20BridgeSource.LiquidityProvider: if (liquidityProviderRegistryAddress === undefined) { throw new Error( 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 e8040c7a86..72ed930048 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -41,6 +41,8 @@ export enum ERC20BridgeSource { MStable = 'mStable', Mooniswap = 'Mooniswap', MultiHop = 'MultiHop', + Swerve = 'Swerve', + SushiSwap = 'SushiSwap', } // tslint:disable: enum-naming @@ -69,6 +71,8 @@ export interface CurveInfo { tokens: string[]; } +export interface SwerveInfo extends CurveInfo {} + // Internal `fillData` field for `Fill` objects. export interface FillData {} @@ -88,6 +92,12 @@ export interface CurveFillData extends FillData { curve: CurveInfo; } +export interface SwerveFillData extends FillData { + fromTokenIdx: number; + toTokenIdx: number; + pool: SwerveInfo; +} + export interface BalancerFillData extends FillData { poolAddress: string; } @@ -96,6 +106,10 @@ export interface UniswapV2FillData extends FillData { tokenAddressPath: string[]; } +export interface SushiSwapFillData extends UniswapV2FillData { + router: string; +} + export interface LiquidityProviderFillData extends FillData { poolAddress: string; } @@ -103,6 +117,7 @@ export interface LiquidityProviderFillData extends FillData { export interface MultiBridgeFillData extends FillData { poolAddress: string; } + export interface BancorFillData extends FillData { path: string[]; networkAddress: string; diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index c26f4db846..8e39d7129c 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -30,6 +30,7 @@ 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'; import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.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'; @@ -49,6 +50,7 @@ export const artifacts = { MultiBridgeSampler: MultiBridgeSampler as ContractArtifact, NativeOrderSampler: NativeOrderSampler as ContractArtifact, SamplerUtils: SamplerUtils 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/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index c2a9c9da2c..aeea772c05 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -27,6 +27,16 @@ const MAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress(); const MAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(MAKER_TOKEN); const TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN); +const DEFAULT_EXCLUDED = [ + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.Curve, + ERC20BridgeSource.Balancer, + ERC20BridgeSource.MStable, + ERC20BridgeSource.Mooniswap, + ERC20BridgeSource.Bancor, + ERC20BridgeSource.Swerve, + ERC20BridgeSource.SushiSwap, +]; // tslint:disable: custom-no-magic-numbers promise-function-async describe('MarketOperationUtils tests', () => { @@ -80,6 +90,8 @@ describe('MarketOperationUtils tests', () => { return ERC20BridgeSource.MStable; case contractAddresses.mooniswapBridge.toLowerCase(): return ERC20BridgeSource.Mooniswap; + case contractAddresses.sushiswapBridge.toLowerCase(): + return ERC20BridgeSource.SushiSwap; default: break; } @@ -238,11 +250,11 @@ describe('MarketOperationUtils tests', () => { [source: string]: Numberish[]; } - const DEFAULT_RATES: RatesBySource = { - [ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES), - [ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES), + const ZERO_RATES: RatesBySource = { + [ERC20BridgeSource.Native]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Eth2Dai]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Uniswap]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Kyber]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.UniswapV2]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Balancer]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Bancor]: _.times(NUM_SAMPLES, () => 0), @@ -251,6 +263,15 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0), + }; + + const DEFAULT_RATES: RatesBySource = { + ...ZERO_RATES, + [ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES), + [ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES), + [ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES), }; interface FillDataBySource { @@ -273,7 +294,19 @@ describe('MarketOperationUtils tests', () => { fromTokenIdx: 0, toTokenIdx: 1, }, + [ERC20BridgeSource.Swerve]: { + pool: { + poolAddress: randomAddress(), + tokens: [TAKER_TOKEN, MAKER_TOKEN], + exchangeFunctionSelector: hexUtils.random(4), + sellQuoteFunctionSelector: hexUtils.random(4), + buyQuoteFunctionSelector: hexUtils.random(4), + }, + fromTokenIdx: 0, + toTokenIdx: 1, + }, [ERC20BridgeSource.LiquidityProvider]: { poolAddress: randomAddress() }, + [ERC20BridgeSource.SushiSwap]: { tokenAddressPath: [] }, }; const DEFAULT_OPS = { @@ -416,14 +449,7 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: [ - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Mooniswap, - ERC20BridgeSource.Bancor, - ], + excludedSources: DEFAULT_EXCLUDED, allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -583,7 +609,7 @@ describe('MarketOperationUtils tests', () => { }); it('can mix convex sources', async () => { - const rates: RatesBySource = {}; + const rates: RatesBySource = { ...DEFAULT_RATES }; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; @@ -852,14 +878,7 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: [ - ERC20BridgeSource.Kyber, - ERC20BridgeSource.UniswapV2, - ERC20BridgeSource.Curve, - ERC20BridgeSource.Balancer, - ERC20BridgeSource.MStable, - ERC20BridgeSource.Mooniswap, - ], + excludedSources: DEFAULT_EXCLUDED, allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -1019,7 +1038,7 @@ describe('MarketOperationUtils tests', () => { }); it('can mix convex sources', async () => { - const rates: RatesBySource = {}; + const rates: RatesBySource = { ...ZERO_RATES }; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; @@ -1049,6 +1068,7 @@ describe('MarketOperationUtils tests', () => { // dropping their effective rates. const nativeFeeRate = 0.06; const rates: RatesBySource = { + ...ZERO_RATES, [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91] [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], @@ -1086,6 +1106,7 @@ describe('MarketOperationUtils tests', () => { // dropping its effective rates. const uniswapFeeRate = 0.2; const rates: RatesBySource = { + ...ZERO_RATES, [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], @@ -1118,7 +1139,7 @@ describe('MarketOperationUtils tests', () => { }); it('fallback orders use different sources', async () => { - const rates: RatesBySource = {}; + const rates: RatesBySource = { ...ZERO_RATES }; rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; @@ -1138,7 +1159,7 @@ describe('MarketOperationUtils tests', () => { }); it('does not create a fallback if below maxFallbackSlippage', async () => { - const rates: RatesBySource = {}; + const rates: RatesBySource = { ...ZERO_RATES }; rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; @@ -1159,7 +1180,7 @@ describe('MarketOperationUtils tests', () => { }); it('batches contiguous bridge sources', async () => { - const rates: RatesBySource = {}; + const rates: RatesBySource = { ...ZERO_RATES }; rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.02, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [0.48, 0.01, 0.01, 0.01]; diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index 9206cd21fd..a130c86c4a 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -28,6 +28,7 @@ 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'; +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 5c7b1bccea..9ef3281121 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -33,6 +33,7 @@ "test/generated-artifacts/MultiBridgeSampler.json", "test/generated-artifacts/NativeOrderSampler.json", "test/generated-artifacts/SamplerUtils.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/addresses.json b/packages/contract-addresses/addresses.json index ab924af787..d3e1ad31dd 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -42,6 +42,7 @@ "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3", "mooniswapBridge": "0x02b7eca484ad960fca3f7709e0b2ac81eec3069c", + "sushiswapBridge": "0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5", "transformers": { "wethTransformer": "0x68c0bb685099dc7cb5c5ce2b26185945b357383e", "payTakerTransformer": "0x49b9df2c58491764cf40cb052dd4243df63622c7", @@ -92,6 +93,7 @@ "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", "mooniswapBridge": "0x0000000000000000000000000000000000000000", + "sushiswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", @@ -142,6 +144,7 @@ "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", "mooniswapBridge": "0x0000000000000000000000000000000000000000", + "sushiswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", @@ -192,6 +195,7 @@ "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "mStableBridge": "0x0000000000000000000000000000000000000000", "mooniswapBridge": "0x0000000000000000000000000000000000000000", + "sushiswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0x9ce35b5ee9e710535e3988e3f8731d9ca9dba17d", "payTakerTransformer": "0x5a53e7b02a83aa9f60ccf4e424f0442c255bc977", @@ -242,6 +246,7 @@ "exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa", "mStableBridge": "0x0000000000000000000000000000000000000000", "mooniswapBridge": "0x0000000000000000000000000000000000000000", + "sushiswapBridge": "0x0000000000000000000000000000000000000000", "transformers": { "wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5", "payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 005134b745..066ec0af12 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -43,6 +43,7 @@ export interface ContractAddresses { exchangeProxyFlashWallet: string; mStableBridge: string; mooniswapBridge: string; + sushiswapBridge: string; transformers: { wethTransformer: string; payTakerTransformer: string; diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index 90fb275ff9..bbe34d55b4 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -400,6 +400,7 @@ export async function runMigrationsAsync( exchangeProxyGovernor: NULL_ADDRESS, mStableBridge: NULL_ADDRESS, mooniswapBridge: NULL_ADDRESS, + sushiswapBridge: NULL_ADDRESS, exchangeProxy: exchangeProxy.address, exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, exchangeProxyTransformerDeployer: txDefaults.from, diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index 5e45b98942..d926e1f291 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -70,6 +70,7 @@ export const docGenConfigs: DocGenConfigs = { 'MultiAssetData', 'StaticCallAssetData', 'MultiAssetDataWithRecursiveDecoding', + 'OrderPrunerPermittedFeeTypes', ], // Some libraries only export types. In those cases, we cannot check if the exported types are part of the // "exported public interface". Thus we add them here and skip those checks.