From c12a10b96e74197a1f7e7875caf2ac672932fea7 Mon Sep 17 00:00:00 2001 From: eobbad <35933488+eobbad@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:54:09 -0400 Subject: [PATCH] Add WooFI support (#513) * Add WooFI interface for sampling * WooFi Sampler * Add mixin * Update bridge adapters * fix some bugs * update transformer_utils * Add BSC support * yarn prettier * Capitalize WOOFI in bridge adapters * Put rebateAddress in a constant, fixed some other stylistic errors * bug fixes * Updated CHANGELOGS & MD docs * Publish - @0x/contracts-erc20@3.3.33 - @0x/contracts-test-utils@5.4.24 - @0x/contracts-treasury@1.4.16 - @0x/contracts-utils@4.8.14 - @0x/contracts-zero-ex@0.36.0 - @0x/asset-swapper@16.64.0 - @0x/contract-addresses@6.17.0 - @0x/contract-wrappers@13.20.5 - @0x/protocol-utils@11.16.0 * Update reference.mdx (#531) Align with `DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE` value from https://github.com/0xProject/0x-api/blob/c74e31c2197ff9231b4856ef6ff0680f22905aa2/src/constants.ts#L26 * code cleanup * remove deusdc curve pool from this pr * Refactor PoolsCache (part 1) [TKR-500] (#525) * Make _refreshPoolCacheIfRequiredAsync type-safe and remove Promise.all * Factor out PoolsCache key logic into a function * Use Map instead of object in PoolsCache and increase the default timeout * Clean up PoolsCache and simplify its public interface * Refactor PoolsCache (part 2) [TKR-500] (#526) * Introduce NoOpPoolsCache and use it in unsupported chains for BeethovenX * Use `NoOpPoolsCache` for `CreamPoolsCache` and `BalancerPoolsCache` on unsupported chains * Remove `getBidAskLiquidityForMakerTakerAssetPairAsync` (#528) * Add transfer approval for quote token for multi-hops and fix buy sampler typo * Use 0x gas api instead of eth gas station api [TKR-502] (#532) * Use 0x gas api instead of eth gas station api * Add integration test for `ProtocolFeeUtils` * Update CHANGELOG.json * Add polygon, fantom, avalanche support * yarn prettier * Updated CHANGELOGS & MD docs * Publish - @0x/asset-swapper@16.65.0 * Remove references to `custom-no-magic-numbers` ts lint rule [TKR-484] (#533) * Remove references to `custom-no-magic-numbers ts` lint rule * Run prettier * resolve Kyu's comments * remove fqt change * Add WooFI interface for sampling * WooFi Sampler * Add mixin * Update bridge adapters * fix some bugs * update transformer_utils * Add BSC support * yarn prettier * Capitalize WOOFI in bridge adapters * Put rebateAddress in a constant, fixed some other stylistic errors * bug fixes * code cleanup * remove deusdc curve pool from this pr * Add transfer approval for quote token for multi-hops and fix buy sampler typo * Add polygon, fantom, avalanche support * yarn prettier * resolve Kyu's comments * remove fqt change * merge types.ts * WOOFi -> WOOFI * fix lerna run lint * Changelog * nit changes Co-authored-by: Github Actions Co-authored-by: Pavel <51318041+pavel-bc@users.noreply.github.com> Co-authored-by: Kyu --- .../src/transformers/FillQuoteTransformer.sol | 2 +- .../bridges/AvalancheBridgeAdapter.sol | 10 ++ .../transformers/bridges/BSCBridgeAdapter.sol | 10 ++ .../transformers/bridges/BridgeProtocols.sol | 1 + .../bridges/FantomBridgeAdapter.sol | 10 ++ .../bridges/PolygonBridgeAdapter.sol | 10 ++ .../bridges/mixins/MixinWOOFi.sol | 136 ++++++++++++++++++ contracts/zero-ex/package.json | 2 +- contracts/zero-ex/test/artifacts.ts | 2 + contracts/zero-ex/test/wrappers.ts | 1 + contracts/zero-ex/tsconfig.json | 1 + packages/asset-swapper/CHANGELOG.json | 9 ++ .../contracts/src/ERC20BridgeSampler.sol | 2 + .../contracts/src/WooPPSampler.sol | 121 ++++++++++++++++ packages/asset-swapper/package.json | 5 +- .../utils/market_operation_utils/constants.ts | 35 +++++ .../utils/market_operation_utils/orders.ts | 8 ++ .../sampler_operations.ts | 47 ++++++ .../src/utils/market_operation_utils/types.ts | 7 + packages/asset-swapper/test/artifacts.ts | 2 + packages/asset-swapper/test/wrappers.ts | 1 + packages/asset-swapper/tsconfig.json | 3 +- .../protocol-utils/src/transformer_utils.ts | 1 + 23 files changed, 421 insertions(+), 5 deletions(-) create mode 100644 contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinWOOFi.sol create mode 100644 packages/asset-swapper/contracts/src/WooPPSampler.sol diff --git a/contracts/zero-ex/contracts/src/transformers/FillQuoteTransformer.sol b/contracts/zero-ex/contracts/src/transformers/FillQuoteTransformer.sol index 93b53c8eb4..d1b271a49e 100644 --- a/contracts/zero-ex/contracts/src/transformers/FillQuoteTransformer.sol +++ b/contracts/zero-ex/contracts/src/transformers/FillQuoteTransformer.sol @@ -313,7 +313,7 @@ contract FillQuoteTransformer is if (success) { results.makerTokenBoughtAmount = abi.decode(resultData, (uint256)); results.takerTokenSoldAmount = takerTokenFillAmount; - } + } } // Fill a single limit order. diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol index 32f49e44c1..26f1d45013 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol @@ -30,6 +30,7 @@ import "./mixins/MixinAaveV2.sol"; import "./mixins/MixinNerve.sol"; import "./mixins/MixinPlatypus.sol"; import "./mixins/MixinUniswapV2.sol"; +import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; contract AvalancheBridgeAdapter is @@ -42,6 +43,7 @@ contract AvalancheBridgeAdapter is MixinNerve, MixinPlatypus, MixinUniswapV2, + MixinWOOFi, MixinZeroExBridge { constructor(IEtherTokenV06 weth) @@ -120,6 +122,14 @@ contract AvalancheBridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.WOOFI) { + if (dryRun) { return (0, true); } + boughtAmount = _tradeWOOFi( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else if (protocolId == BridgeProtocols.UNKNOWN) { if (dryRun) { return (0, true); } boughtAmount = _tradeZeroExBridge( diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BSCBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BSCBridgeAdapter.sol index e93476a7c0..acec88e5a4 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BSCBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BSCBridgeAdapter.sol @@ -29,6 +29,7 @@ import "./mixins/MixinKyberDmm.sol"; import "./mixins/MixinMooniswap.sol"; import "./mixins/MixinNerve.sol"; import "./mixins/MixinUniswapV2.sol"; +import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; contract BSCBridgeAdapter is @@ -40,6 +41,7 @@ contract BSCBridgeAdapter is MixinMooniswap, MixinNerve, MixinUniswapV2, + MixinWOOFi, MixinZeroExBridge { constructor(IEtherTokenV06 weth) @@ -111,6 +113,14 @@ contract BSCBridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.WOOFI) { + if (dryRun) { return (0, true); } + boughtAmount = _tradeWOOFi( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else if (protocolId == BridgeProtocols.UNKNOWN) { if (dryRun) { return (0, true); } boughtAmount = _tradeZeroExBridge( diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol index feaa19f248..289906e7e4 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -58,4 +58,5 @@ library BridgeProtocols { uint128 internal constant BANCORV3 = 28; uint128 internal constant VELODROME = 29; uint128 internal constant SYNTHETIX = 30; + uint128 internal constant WOOFI = 31; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/FantomBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/FantomBridgeAdapter.sol index 4f42614460..6e3edf43f7 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/FantomBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/FantomBridgeAdapter.sol @@ -28,6 +28,7 @@ import "./mixins/MixinCurve.sol"; import "./mixins/MixinCurveV2.sol"; import "./mixins/MixinNerve.sol"; import "./mixins/MixinUniswapV2.sol"; +import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; contract FantomBridgeAdapter is @@ -38,6 +39,7 @@ contract FantomBridgeAdapter is MixinCurveV2, MixinNerve, MixinUniswapV2, + MixinWOOFi, MixinZeroExBridge { constructor(IEtherTokenV06 weth) @@ -103,6 +105,14 @@ contract FantomBridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.WOOFI) { + if (dryRun) { return (0, true); } + boughtAmount = _tradeWOOFi( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else if (protocolId == BridgeProtocols.UNKNOWN) { if (dryRun) { return (0, true); } boughtAmount = _tradeZeroExBridge( diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol index f21be7aa90..7b7f945c7f 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol @@ -34,6 +34,7 @@ import "./mixins/MixinMStable.sol"; import "./mixins/MixinNerve.sol"; import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinUniswapV3.sol"; +import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; contract PolygonBridgeAdapter is @@ -50,6 +51,7 @@ contract PolygonBridgeAdapter is MixinNerve, MixinUniswapV2, MixinUniswapV3, + MixinWOOFi, MixinZeroExBridge { constructor(IEtherTokenV06 weth) @@ -157,6 +159,14 @@ contract PolygonBridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.WOOFI) { + if (dryRun) { return (0, true); } + boughtAmount = _tradeWOOFi( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else if (protocolId == BridgeProtocols.UNKNOWN) { if (dryRun) { return (0, true); } boughtAmount = _tradeZeroExBridge( diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinWOOFi.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinWOOFi.sol new file mode 100644 index 0000000000..f1c19c1cf2 --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinWOOFi.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + Copyright 2020 ZeroEx Intl. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity ^0.6; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "../IBridgeAdapter.sol"; +import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; + +/// @dev WooFI pool interface. +interface IWooPP { + function quoteToken() external view returns (address); + + function sellBase( + address baseToken, + uint256 baseAmount, + uint256 minQuoteAmount, + address to, + address rebateTo + ) external returns (uint256 quoteAmount); + + function sellQuote( + address baseToken, + uint256 quoteAmount, + uint256 minBaseAmount, + address to, + address rebateTo + ) external returns (uint256 baseAmount); + + /// @dev Query the amount for selling the base token amount. + /// @param baseToken the base token to sell + /// @param baseAmount the amount to sell + /// @return quoteAmount the swapped quote amount + function querySellBase( + address baseToken, + uint256 baseAmount + ) external view returns (uint256 quoteAmount); + +} + +contract MixinWOOFi{ + + using LibERC20TokenV06 for IERC20TokenV06; + using LibERC20TokenV06 for IEtherTokenV06; + using LibSafeMathV06 for uint256; + + address constant rebateAddress = 0xBfdcBB4C05843163F491C24f9c0019c510786304; + + // /// @dev Swaps an exact amount of input tokens for as many output tokens as possible. + // /// @param _amountIn Amount of input tokens to send + // /// @param _minAmountOut The minimum amount of output tokens that must be received for the transaction not to revert. + // /// @param _tokenIn Input token + // /// @param _tokenOut Output token + // /// @param _to recipient of tokens + // /// @param pool WOOFi pool where the swap will happen + function _tradeWOOFi( + IERC20TokenV06 sellToken, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) + public + returns (uint256 boughtAmount) + { + (IWooPP _pool) = abi.decode(bridgeData, (IWooPP)); + uint256 beforeBalance = buyToken.balanceOf(address(this)); + + sellToken.approveIfBelow(address(_pool), sellAmount); + + _swap( + sellAmount, + address(sellToken), + address(buyToken), + _pool + ); + boughtAmount = buyToken.balanceOf(address(this)).safeSub(beforeBalance); + } + + function _swap( + uint _amountIn, + address _tokenIn, + address _tokenOut, + IWooPP pool + ) internal { + address quoteToken = pool.quoteToken(); + if (_tokenIn == quoteToken) { + pool.sellQuote( + _tokenOut, + _amountIn, + 1, + address(this), + rebateAddress + ); + } else if (_tokenOut == quoteToken) { + pool.sellBase( + _tokenIn, + _amountIn, + 1, + address(this), + rebateAddress + ); + } else { + uint256 quoteAmount = pool.sellBase( + _tokenIn, + _amountIn, + 0, + address(this), + rebateAddress + ); + IERC20TokenV06(pool.quoteToken()).approveIfBelow(address(pool), quoteAmount); + pool.sellQuote( + _tokenOut, + quoteAmount, + 1, + address(this), + rebateAddress + ); + } + } + +} \ No newline at end of file diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index ec4a41db12..24d08e3b16 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -43,7 +43,7 @@ "config": { "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinVelodrome|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json" + "abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinVelodrome|MixinWOOFi|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json" }, "repository": { "type": "git", diff --git a/contracts/zero-ex/test/artifacts.ts b/contracts/zero-ex/test/artifacts.ts index 82b7284332..95977bcd3b 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -129,6 +129,7 @@ import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json'; import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json'; import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json'; import * as MixinVelodrome from '../test/generated-artifacts/MixinVelodrome.json'; +import * as MixinWOOFi from '../test/generated-artifacts/MixinWOOFi.json'; import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json'; import * as MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json'; import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json'; @@ -353,6 +354,7 @@ export const artifacts = { MixinUniswapV2: MixinUniswapV2 as ContractArtifact, MixinUniswapV3: MixinUniswapV3 as ContractArtifact, MixinVelodrome: MixinVelodrome as ContractArtifact, + MixinWOOFi: MixinWOOFi as ContractArtifact, MixinZeroExBridge: MixinZeroExBridge as ContractArtifact, IERC1155Token: IERC1155Token as ContractArtifact, IERC721Token: IERC721Token as ContractArtifact, diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index ca034da2a4..3f55018b01 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -127,6 +127,7 @@ export * from '../test/generated-wrappers/mixin_uniswap'; export * from '../test/generated-wrappers/mixin_uniswap_v2'; export * from '../test/generated-wrappers/mixin_uniswap_v3'; export * from '../test/generated-wrappers/mixin_velodrome'; +export * from '../test/generated-wrappers/mixin_w_o_o_fi'; export * from '../test/generated-wrappers/mixin_zero_ex_bridge'; export * from '../test/generated-wrappers/mooniswap_liquidity_provider'; export * from '../test/generated-wrappers/multiplex_feature'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index cb6dfa4f9e..8ae8f5db84 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -166,6 +166,7 @@ "test/generated-artifacts/MixinUniswapV2.json", "test/generated-artifacts/MixinUniswapV3.json", "test/generated-artifacts/MixinVelodrome.json", + "test/generated-artifacts/MixinWOOFi.json", "test/generated-artifacts/MixinZeroExBridge.json", "test/generated-artifacts/MooniswapLiquidityProvider.json", "test/generated-artifacts/MultiplexFeature.json", diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index fe5171e287..ce21b29361 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "16.66.0", + "changes": [ + { + "note": "Add WOOFi support", + "pr": 513 + } + ] + }, { "version": "16.65.0", "changes": [ diff --git a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol index cdb20615f2..01195bfbe1 100644 --- a/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol +++ b/packages/asset-swapper/contracts/src/ERC20BridgeSampler.sol @@ -45,6 +45,7 @@ import "./UniswapSampler.sol"; import "./UniswapV2Sampler.sol"; import "./UniswapV3Sampler.sol"; import "./VelodromeSampler.sol"; +import "./WooPPSampler.sol"; import "./UtilitySampler.sol"; @@ -74,6 +75,7 @@ contract ERC20BridgeSampler is UniswapV2Sampler, UniswapV3Sampler, VelodromeSampler, + WooPPSampler, UtilitySampler { diff --git a/packages/asset-swapper/contracts/src/WooPPSampler.sol b/packages/asset-swapper/contracts/src/WooPPSampler.sol new file mode 100644 index 0000000000..4d40c6dde7 --- /dev/null +++ b/packages/asset-swapper/contracts/src/WooPPSampler.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.6; +pragma experimental ABIEncoderV2; +import "./SamplerUtils.sol"; +import "./ApproximateBuys.sol"; + +interface IWooPP { + /// @dev get the quote token address (immutable) + /// @return address of quote token + function quoteToken() external view returns (address); + + /// @dev Query the amount for selling the base token amount. + /// @param baseToken the base token to sell + /// @param baseAmount the amount to sell + /// @return quoteAmount the swapped quote amount + function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount); + + /// @dev Query the amount for selling the quote token. + /// @param baseToken the base token to receive (buy) + /// @param quoteAmount the amount to sell + /// @return baseAmount the swapped base token amount + function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount); +} + +contract WooPPSampler is SamplerUtils, ApproximateBuys{ + + function query( + uint amountIn, + address tokenIn, + address tokenOut, + address pool + ) internal view returns (uint256 amountOut) { + if (amountIn == 0) { + return 0; + } + address quoteToken = IWooPP(pool).quoteToken(); + if (tokenIn == quoteToken) { + amountOut = IWooPP(pool).querySellQuote(tokenOut, amountIn); + } else if (tokenOut == quoteToken) { + amountOut = IWooPP(pool).querySellBase(tokenIn, amountIn); + } else { + uint quoteAmount = IWooPP(pool).querySellBase(tokenIn, amountIn); + amountOut = IWooPP(pool).querySellQuote(tokenOut, quoteAmount); + } + } + + /// @dev Sample sell quotes from WooFI. + /// @param pool Address of the pool we are sampling from + /// @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 (sorted in ascending order). + /// @return makerTokenAmounts Maker amounts bought at each taker token + /// amount. + function sampleSellsFromWooPP( + address pool, + address takerToken, + address makerToken, + uint256[] memory takerTokenAmounts + ) + public + view + returns (uint256[] memory makerTokenAmounts) + { + uint256 numSamples = takerTokenAmounts.length; + makerTokenAmounts = new uint256[](numSamples); + for (uint256 i = 0; i < numSamples; i++) { + makerTokenAmounts[i] = query(takerTokenAmounts[i], takerToken, makerToken, pool); + + if (makerTokenAmounts[i] == 0) { + break; + } + } + } + + /// @dev Sample buy quotes from WooFI. + /// @param pool Address of the pool we are sampling from + /// @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 (sorted in ascending order). + /// @return takerTokenAmounts Taker amounts bought at each taker token + /// amount. + function sampleBuysFromWooPP( + address pool, + address takerToken, + address makerToken, + uint256[] memory makerTokenAmounts + ) + public + view + returns (uint256[] memory takerTokenAmounts) + { + uint256 numSamples = makerTokenAmounts.length; + takerTokenAmounts = _sampleApproximateBuys( + ApproximateBuyQuoteOpts({ + takerTokenData: abi.encode(pool,takerToken, makerToken), + makerTokenData: abi.encode(pool, makerToken, takerToken), + getSellQuoteCallback: _sampleSellForApproximateBuyFromWoofi + }), + makerTokenAmounts + ); + } + + function _sampleSellForApproximateBuyFromWoofi( + bytes memory takerTokenData, + bytes memory makerTokenData, + uint256 sellAmount + ) internal view returns (uint256) { + (address _pool, address _takerToken, address _makerToken) = abi.decode(takerTokenData, (address, address, address)); + (bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector( + this.sampleSellsFromWooPP.selector, + _pool, + _takerToken, + _makerToken, + _toSingleValueArray(sellAmount) + )); + if(!success) { + return 0; + } + return abi.decode(resultData, (uint256[]))[0]; + } +} diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 9a0f434bf4..875188577a 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -40,7 +40,7 @@ "config": { "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json", + "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler|WooPPSampler).json", "postpublish": { "assets": [] } @@ -89,7 +89,8 @@ "graphql": "^15.4.0", "graphql-request": "^3.4.0", "heartbeats": "^5.0.1", - "lodash": "^4.17.11" + "lodash": "^4.17.11", + "msw": "^0.44.2" }, "devDependencies": { "@0x/abi-gen": "^5.8.0", 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 8a5797b7cf..484e4acd4f 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -35,6 +35,7 @@ import { SynthetixFillData, UniswapV2FillData, UniswapV3FillData, + WOOFiFillData, } from './types'; // tslint:disable: no-bitwise @@ -154,6 +155,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.BiSwap, ERC20BridgeSource.MDex, ERC20BridgeSource.KnightSwap, + ERC20BridgeSource.WOOFi, ]), [ChainId.Polygon]: new SourceFilters([ ERC20BridgeSource.SushiSwap, @@ -176,6 +178,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.UniswapV3, ERC20BridgeSource.Synapse, ERC20BridgeSource.MeshSwap, + ERC20BridgeSource.WOOFi, ]), [ChainId.Avalanche]: new SourceFilters([ ERC20BridgeSource.MultiHop, @@ -189,6 +192,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.Synapse, ERC20BridgeSource.GMX, ERC20BridgeSource.Platypus, + ERC20BridgeSource.WOOFi, ]), [ChainId.Fantom]: new SourceFilters([ ERC20BridgeSource.MultiHop, @@ -202,6 +206,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.SushiSwap, ERC20BridgeSource.Synapse, ERC20BridgeSource.Yoshi, + ERC20BridgeSource.WOOFi, ]), [ChainId.Celo]: new SourceFilters([ ERC20BridgeSource.UbeSwap, @@ -302,6 +307,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.BiSwap, ERC20BridgeSource.MDex, ERC20BridgeSource.KnightSwap, + ERC20BridgeSource.WOOFi, ]), [ChainId.Polygon]: new SourceFilters([ ERC20BridgeSource.SushiSwap, @@ -324,6 +330,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.UniswapV3, ERC20BridgeSource.Synapse, ERC20BridgeSource.MeshSwap, + ERC20BridgeSource.WOOFi, ]), [ChainId.Avalanche]: new SourceFilters([ ERC20BridgeSource.MultiHop, @@ -337,6 +344,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.Synapse, ERC20BridgeSource.GMX, ERC20BridgeSource.Platypus, + ERC20BridgeSource.WOOFi, ]), [ChainId.Fantom]: new SourceFilters([ ERC20BridgeSource.MultiHop, @@ -350,6 +358,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.SushiSwap, ERC20BridgeSource.Synapse, ERC20BridgeSource.Yoshi, + ERC20BridgeSource.WOOFi, ]), [ChainId.Celo]: new SourceFilters([ ERC20BridgeSource.UbeSwap, @@ -542,6 +551,7 @@ export const BSC_TOKENS = { pBTC: '0xed28a457a5a76596ac48d87c0f577020f6ea1c4c', nUSD: '0x23b891e5c62e0955ae2bd185990103928ab817b3', BSW: '0x965F527D9159dCe6288a2219DB51fc6Eef120dD1', + WOO: '0x4691937a7508860f876c9c0a2a617e7d9e945d4b', }; export const POLYGON_TOKENS = { @@ -561,6 +571,7 @@ export const POLYGON_TOKENS = { WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb', nUSD: '0xb6c473756050de474286bed418b77aeac39b02af', ANY: '0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8', + WOO: '0x1b815d120b3ef02039ee11dc2d33de7aa4a8c603', }; export const AVALANCHE_TOKENS = { @@ -587,6 +598,7 @@ export const AVALANCHE_TOKENS = { UST: '0xb599c3590f42f8f995ecfa0f85d2980b76862fc1', FRAX: '0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64', YUSD: '0x111111111111ed1d73f860f57b2798b683f2d325', + WOO: '0xabc9547b534519ff73921b1fba6e672b5f58d083', }; export const CELO_TOKENS = { @@ -646,6 +658,7 @@ export const FANTOM_TOKENS = { gWBTC: '0x38aca5484b8603373acc6961ecd57a6a594510a3', gCRV: '0x690754a168b022331caa2467207c61919b3f8a98', gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe', + WOO: '0x6626c47c00f1d87902fc13eecfac3ed06d5e8d8a', }; export const OPTIMISM_TOKENS = { @@ -864,6 +877,16 @@ export const PLATYPUS_AVALANCHE_POOLS = { sAVAX: '0x4658ea7e9960d6158a261104aaa160cc953bb6ba', }; +export const WOOFI_POOL_BY_CHAIN_ID = valueByChainId( + { + [ChainId.BSC]: '0xbf365ce9cfcb2d5855521985e351ba3bcf77fd3f', + [ChainId.Fantom]: '0x9503e7517d3c5bc4f9e4a1c6ae4f8b33ac2546f2', + [ChainId.Avalanche]: '0x1df3009c57a8b143c6246149f00b090bce3b8f88', + [ChainId.Polygon]: '0x7400b665c8f4f3a951a99f1ee9872efb8778723d', + }, + NULL_ADDRESS, +); + export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId( { [ChainId.Mainnet]: [ @@ -2645,7 +2668,19 @@ export const DEFAULT_GAS_SCHEDULE: Required = { [ERC20BridgeSource.CheeseSwap]: uniswapV2CloneGasSchedule, [ERC20BridgeSource.WaultSwap]: uniswapV2CloneGasSchedule, [ERC20BridgeSource.ACryptos]: fillData => (fillData as CurveFillData).pool.gasSchedule, + [ERC20BridgeSource.WOOFi]: (fillData?: FillData) => { + const woofiFillData = fillData as WOOFiFillData; + const quoteTokenAddresses = [BSC_TOKENS.USDT, AVALANCHE_TOKENS.nUSDC, FANTOM_TOKENS.USDC, POLYGON_TOKENS.USDC]; + if ( + quoteTokenAddresses.includes(woofiFillData.takerToken) || + quoteTokenAddresses.includes(woofiFillData.makerToken) + ) { + return 500e3; + } else { + return 100e4; + } + }, // // Polygon // 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 73b3ff0ba7..433715a291 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -43,6 +43,7 @@ import { UniswapV3FillData, UniswapV3PathAmount, VelodromeFillData, + WOOFiFillData, } from './types'; // tslint:disable completed-docs @@ -213,6 +214,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome'); case ERC20BridgeSource.Synthetix: return encodeBridgeSourceId(BridgeProtocol.Synthetix, 'Synthetix'); + case ERC20BridgeSource.WOOFi: + return encodeBridgeSourceId(BridgeProtocol.WOOFi, 'WOOFi'); default: throw new Error(AggregationError.NoBridgeForSource); } @@ -402,6 +405,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder fillData.makerTokenSymbolBytes32, ]); break; + case ERC20BridgeSource.WOOFi: + const woofiFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([woofiFillData.poolAddress]); + break; default: throw new Error(AggregationError.NoBridgeForSource); } @@ -529,6 +536,7 @@ export const BRIDGE_ENCODERS: { [ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'), [ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'), [ERC20BridgeSource.Synthetix]: AbiEncoder.create('(address,bytes32,bytes32)'), + [ERC20BridgeSource.WOOFi]: AbiEncoder.create('(address)'), }; function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] { diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index d977429d17..a4387bd43b 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 @@ -52,6 +52,7 @@ import { UNISWAPV1_ROUTER_BY_CHAIN_ID, UNISWAPV3_CONFIG_BY_CHAIN_ID, VELODROME_ROUTER_BY_CHAIN_ID, + WOOFI_POOL_BY_CHAIN_ID, ZERO_AMOUNT, } from './constants'; import { getGeistInfoForPair } from './geist_utils'; @@ -99,6 +100,7 @@ import { UniswapV2FillData, UniswapV3FillData, VelodromeFillData, + WOOFiFillData, } from './types'; /** @@ -1364,6 +1366,35 @@ export class SamplerOperations { }, }); } + public getWOOFiSellQuotes( + poolAddress: string, + takerToken: string, + makerToken: string, + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + fillData: { poolAddress, takerToken, makerToken }, + source: ERC20BridgeSource.WOOFi, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromWooPP, + params: [poolAddress, takerToken, makerToken, makerFillAmounts], + }); + } + + public getWOOFiBuyQuotes( + poolAddress: string, + takerToken: string, + makerToken: string, + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + fillData: { poolAddress, takerToken, makerToken }, + source: ERC20BridgeSource.WOOFi, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromWooPP, + params: [poolAddress, takerToken, makerToken, makerFillAmounts], + }); + } /** * Returns the best price for the native token @@ -1800,6 +1831,14 @@ export class SamplerOperations { takerFillAmounts, ); } + case ERC20BridgeSource.WOOFi: { + return this.getWOOFiSellQuotes( + WOOFI_POOL_BY_CHAIN_ID[this.chainId], + takerToken, + makerToken, + takerFillAmounts, + ); + } default: throw new Error(`Unsupported sell sample source: ${source}`); } @@ -2140,6 +2179,14 @@ export class SamplerOperations { makerFillAmounts, ); } + case ERC20BridgeSource.WOOFi: { + return this.getWOOFiBuyQuotes( + WOOFI_POOL_BY_CHAIN_ID[this.chainId], + takerToken, + makerToken, + 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 3504b0d899..b3e297d74b 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -69,6 +69,7 @@ export enum ERC20BridgeSource { Synapse = 'Synapse', BancorV3 = 'BancorV3', Synthetix = 'Synthetix', + WOOFi = 'WOOFi', // BSC only PancakeSwap = 'PancakeSwap', PancakeSwapV2 = 'PancakeSwap_V2', @@ -373,6 +374,12 @@ export interface PlatypusFillData extends FillData { tokenAddressPath: string[]; } +export interface WOOFiFillData extends FillData { + poolAddress: string; + takerToken: string; + makerToken: string; +} + export interface VelodromeFillData extends FillData { router: string; stable: boolean; diff --git a/packages/asset-swapper/test/artifacts.ts b/packages/asset-swapper/test/artifacts.ts index b78346a3fb..4ebf5bba93 100644 --- a/packages/asset-swapper/test/artifacts.ts +++ b/packages/asset-swapper/test/artifacts.ts @@ -51,6 +51,7 @@ import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler. import * as UniswapV3Sampler from '../test/generated-artifacts/UniswapV3Sampler.json'; import * as UtilitySampler from '../test/generated-artifacts/UtilitySampler.json'; import * as VelodromeSampler from '../test/generated-artifacts/VelodromeSampler.json'; +import * as WooPPSampler from '../test/generated-artifacts/WooPPSampler.json'; export const artifacts = { ApproximateBuys: ApproximateBuys as ContractArtifact, BalanceChecker: BalanceChecker as ContractArtifact, @@ -84,6 +85,7 @@ export const artifacts = { UniswapV3Sampler: UniswapV3Sampler as ContractArtifact, UtilitySampler: UtilitySampler as ContractArtifact, VelodromeSampler: VelodromeSampler as ContractArtifact, + WooPPSampler: WooPPSampler as ContractArtifact, IBalancer: IBalancer as ContractArtifact, IBalancerV2Vault: IBalancerV2Vault as ContractArtifact, IBancor: IBancor as ContractArtifact, diff --git a/packages/asset-swapper/test/wrappers.ts b/packages/asset-swapper/test/wrappers.ts index a987fc25b5..3850f01359 100644 --- a/packages/asset-swapper/test/wrappers.ts +++ b/packages/asset-swapper/test/wrappers.ts @@ -49,3 +49,4 @@ export * from '../test/generated-wrappers/uniswap_v2_sampler'; export * from '../test/generated-wrappers/uniswap_v3_sampler'; export * from '../test/generated-wrappers/utility_sampler'; export * from '../test/generated-wrappers/velodrome_sampler'; +export * from '../test/generated-wrappers/woo_p_p_sampler'; diff --git a/packages/asset-swapper/tsconfig.json b/packages/asset-swapper/tsconfig.json index 18dea0a5b3..b656cc4425 100644 --- a/packages/asset-swapper/tsconfig.json +++ b/packages/asset-swapper/tsconfig.json @@ -51,6 +51,7 @@ "test/generated-artifacts/UniswapV2Sampler.json", "test/generated-artifacts/UniswapV3Sampler.json", "test/generated-artifacts/UtilitySampler.json", - "test/generated-artifacts/VelodromeSampler.json" + "test/generated-artifacts/VelodromeSampler.json", + "test/generated-artifacts/WooPPSampler.json" ] } diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index f4bd1a96a9..e6a869b458 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -141,6 +141,7 @@ export enum BridgeProtocol { BancorV3, Velodrome, Synthetix, + WOOFi, } // tslint:enable: enum-naming