diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index b7dc3181f0..0599747c73 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "0.27.0", + "changes": [ + { + "note": "Add `Clipper` as a custom liquidity source" + } + ] + }, { "version": "0.26.0", "changes": [ diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index 0b8d5937e6..60ecb0c183 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -25,6 +25,7 @@ import "./BridgeProtocols.sol"; import "./mixins/MixinBalancer.sol"; import "./mixins/MixinBalancerV2.sol"; import "./mixins/MixinBancor.sol"; +import "./mixins/MixinClipper.sol"; import "./mixins/MixinCoFiX.sol"; import "./mixins/MixinCurve.sol"; import "./mixins/MixinCurveV2.sol"; @@ -50,6 +51,7 @@ contract BridgeAdapter is MixinBalancer, MixinBalancerV2, MixinBancor, + MixinClipper, MixinCoFiX, MixinCurve, MixinCurveV2, @@ -75,6 +77,7 @@ contract BridgeAdapter is MixinBalancer() MixinBalancerV2() MixinBancor(weth) + MixinClipper(weth) MixinCoFiX() MixinCurve(weth) MixinCurveV2() @@ -245,6 +248,13 @@ contract BridgeAdapter is sellAmount, order.bridgeData ); + } else if (protocolId == BridgeProtocols.CLIPPER) { + boughtAmount = _tradeClipper( + sellToken, + buyToken, + sellAmount, + order.bridgeData + ); } else { boughtAmount = _tradeZeroExBridge( sellToken, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol index c7b2cc9f4b..378857da21 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -49,4 +49,5 @@ library BridgeProtocols { uint128 internal constant KYBERDMM = 19; uint128 internal constant CURVEV2 = 20; uint128 internal constant LIDO = 21; + uint128 internal constant CLIPPER = 22; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClipper.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClipper.sol new file mode 100644 index 0000000000..f88a9eef0b --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClipper.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + + Copyright 2021 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.6.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "../IBridgeAdapter.sol"; +import "../../../vendor/ILiquidityProvider.sol"; + +contract MixinClipper { + + using LibERC20TokenV06 for IERC20TokenV06; + + /// @dev Mainnet address of the WETH contract. + IEtherTokenV06 private immutable WETH; + + constructor(IEtherTokenV06 weth) + public + { + WETH = weth; + } + + function _tradeClipper( + IERC20TokenV06 sellToken, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) + internal + returns (uint256 boughtAmount) + { + // We can only use ETH with Clipper, no WETH available + (ILiquidityProvider clipper, bytes memory auxiliaryData) = + abi.decode(bridgeData, (ILiquidityProvider, bytes)); + + if (sellToken == WETH) { + boughtAmount = _executeSellEthForToken( + clipper, + buyToken, + sellAmount, + auxiliaryData + ); + } else if (buyToken == WETH) { + boughtAmount = _executeSellTokenForEth( + clipper, + sellToken, + sellAmount, + auxiliaryData + ); + } else { + boughtAmount = _executeSellTokenForToken( + clipper, + sellToken, + buyToken, + sellAmount, + auxiliaryData + ); + } + + return boughtAmount; + } + + function _executeSellEthForToken( + ILiquidityProvider clipper, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory auxiliaryData + ) + private + returns (uint256 boughtAmount) + { + // Clipper requires ETH and doesn't support WETH + WETH.withdraw(sellAmount); + boughtAmount = clipper.sellEthForToken{ value: sellAmount }( + buyToken, + address(this), + 1, + auxiliaryData + ); + } + + function _executeSellTokenForEth( + ILiquidityProvider clipper, + IERC20TokenV06 sellToken, + uint256 sellAmount, + bytes memory auxiliaryData + ) + private + returns (uint256 boughtAmount) + { + // Optimization: We can transfer the tokens into clipper rather than + // have an allowance updated + sellToken.compatTransfer(address(clipper), sellAmount); + + boughtAmount = clipper.sellTokenForEth( + sellToken, + payable(address(this)), + 1, + auxiliaryData + ); + + // we want WETH for possible future trades + WETH.deposit{ value: boughtAmount }(); + } + + function _executeSellTokenForToken( + ILiquidityProvider clipper, + IERC20TokenV06 sellToken, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory auxiliaryData + ) + private + returns (uint256 boughtAmount) + { + // Optimization: We can transfer the tokens into clipper rather than + // have an allowance updated + sellToken.compatTransfer(address(clipper), sellAmount); + + boughtAmount = clipper.sellTokenForToken( + sellToken, + buyToken, + address(this), + 1, + auxiliaryData + ); + } +} diff --git a/contracts/zero-ex/package.json b/contracts/zero-ex/package.json index a43273d689..d2659849cc 100644 --- a/contracts/zero-ex/package.json +++ b/contracts/zero-ex/package.json @@ -43,7 +43,7 @@ "config": { "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json" + "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinClipper|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|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 f310260b19..860cbeb10a 100644 --- a/contracts/zero-ex/test/artifacts.ts +++ b/contracts/zero-ex/test/artifacts.ts @@ -82,6 +82,7 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json'; import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json'; import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json'; +import * as MixinClipper from '../test/generated-artifacts/MixinClipper.json'; import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json'; import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json'; import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json'; @@ -257,6 +258,7 @@ export const artifacts = { MixinBalancer: MixinBalancer as ContractArtifact, MixinBalancerV2: MixinBalancerV2 as ContractArtifact, MixinBancor: MixinBancor as ContractArtifact, + MixinClipper: MixinClipper as ContractArtifact, MixinCoFiX: MixinCoFiX as ContractArtifact, MixinCryptoCom: MixinCryptoCom as ContractArtifact, MixinCurve: MixinCurve as ContractArtifact, diff --git a/contracts/zero-ex/test/wrappers.ts b/contracts/zero-ex/test/wrappers.ts index 7b1dbbbfa3..ed85df16d8 100644 --- a/contracts/zero-ex/test/wrappers.ts +++ b/contracts/zero-ex/test/wrappers.ts @@ -80,6 +80,7 @@ export * from '../test/generated-wrappers/meta_transactions_feature'; export * from '../test/generated-wrappers/mixin_balancer'; export * from '../test/generated-wrappers/mixin_balancer_v2'; export * from '../test/generated-wrappers/mixin_bancor'; +export * from '../test/generated-wrappers/mixin_clipper'; export * from '../test/generated-wrappers/mixin_co_fi_x'; export * from '../test/generated-wrappers/mixin_crypto_com'; export * from '../test/generated-wrappers/mixin_curve'; diff --git a/contracts/zero-ex/tsconfig.json b/contracts/zero-ex/tsconfig.json index be5b96aa22..6aaad5652b 100644 --- a/contracts/zero-ex/tsconfig.json +++ b/contracts/zero-ex/tsconfig.json @@ -113,6 +113,7 @@ "test/generated-artifacts/MixinBalancer.json", "test/generated-artifacts/MixinBalancerV2.json", "test/generated-artifacts/MixinBancor.json", + "test/generated-artifacts/MixinClipper.json", "test/generated-artifacts/MixinCoFiX.json", "test/generated-artifacts/MixinCryptoCom.json", "test/generated-artifacts/MixinCurve.json", diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 6a35f48d0f..3247da6330 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,17 @@ [ + { + "version": "16.24.0", + "changes": [ + { + "note": "Add `Clipper` as a custom liquidity source", + "pr": 299 + }, + { + "note": "Added `Curve` `Tricrypto2` and `ESD` v2", + "pr": 302 + } + ] + }, { "version": "16.23.1", "changes": [ 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 4fe2fb5853..4674d590e1 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -98,6 +98,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.UniswapV3, ERC20BridgeSource.CurveV2, ERC20BridgeSource.ShibaSwap, + ERC20BridgeSource.Clipper, ]), [ChainId.Ropsten]: new SourceFilters([ ERC20BridgeSource.Kyber, @@ -198,6 +199,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId( ERC20BridgeSource.UniswapV3, ERC20BridgeSource.CurveV2, ERC20BridgeSource.ShibaSwap, + ERC20BridgeSource.Clipper, ]), [ChainId.Ropsten]: new SourceFilters([ ERC20BridgeSource.Kyber, @@ -373,6 +375,8 @@ export const MAINNET_TOKENS = { FRAX: '0x853d955acef822db058eb8505911ed77f175b99e', LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0', FEI: '0x956f47f50a910163d8bf957cf5846d573e7f87ca', + DSU: '0x605d26fbd5be761089281d5cec2ce86eea667109', + ESS: '0x24ae124c4cc33d6791f8e8b63520ed7107ac8b3e', }; export const BSC_TOKENS = { @@ -448,10 +452,12 @@ export const CURVE_POOLS = { FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b', LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca', BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a', + DSU3CRV: '0x6ec80df362d7042c50d4469bcfbc174c9dd9109a', }; export const CURVE_V2_POOLS = { tricrypto: '0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5', + tricrypto2: '0xd51a44d3fae010294c616388b506acda1bfaae46', }; export const CURVE_POLYGON_POOLS = { @@ -847,6 +853,11 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = { pool: CURVE_POOLS.ankreth, gasSchedule: 125e3, }), + [CURVE_POOLS.DSU3CRV]: createCurveMetaTriPool({ + tokens: [MAINNET_TOKENS.DSU], + pool: CURVE_POOLS.DSU3CRV, + gasSchedule: 387e3, + }), }; export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = { @@ -855,6 +866,11 @@ export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = { pool: CURVE_V2_POOLS.tricrypto, gasSchedule: 300e3, }), + [CURVE_V2_POOLS.tricrypto2]: createCurveExchangeV2Pool({ + tokens: [MAINNET_TOKENS.USDT, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.WETH], + pool: CURVE_V2_POOLS.tricrypto2, + gasSchedule: 300e3, + }), }; export const CURVE_POLYGON_INFOS: { [name: string]: CurveInfo } = { @@ -1410,6 +1426,22 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId( }, ); +export const CLIPPER_INFO_BY_CHAIN = valueByChainId( + { + [ChainId.Mainnet]: { + poolAddress: '0xe82906b6b1b04f631d126c974af57a3a7b6a99d9', + tokens: [ + MAINNET_TOKENS.WETH, // technically ETH but our sampler and mixin handle this + MAINNET_TOKENS.WBTC, + MAINNET_TOKENS.USDC, + MAINNET_TOKENS.USDT, + MAINNET_TOKENS.DAI, + ], + }, + }, + { poolAddress: NULL_ADDRESS, tokens: [] }, +); + export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer'; export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_MAX_POOLS_FETCHED = 3; @@ -1640,6 +1672,7 @@ export const DEFAULT_GAS_SCHEDULE: Required = { return gas; }, [ERC20BridgeSource.Lido]: () => 226e3, + [ERC20BridgeSource.Clipper]: () => 170e3, // // BSC 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 0603fe824e..84bff3b447 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -3,7 +3,7 @@ import { AbiEncoder, BigNumber } from '@0x/utils'; import { AssetSwapperContractAddresses, MarketOperation } from '../../types'; -import { MAX_UINT256, ZERO_AMOUNT } from './constants'; +import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; import { AggregationError, BalancerFillData, @@ -180,6 +180,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap'); case ERC20BridgeSource.ACryptos: return encodeBridgeSourceId(BridgeProtocol.Curve, 'ACryptoS'); + case ERC20BridgeSource.Clipper: + return encodeBridgeSourceId(BridgeProtocol.Clipper, 'Clipper'); default: throw new Error(AggregationError.NoBridgeForSource); } @@ -318,6 +320,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder const lidoFillData = (order as OptimizedMarketBridgeOrder).fillData; bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]); break; + case ERC20BridgeSource.Clipper: + const clipperFillData = (order as OptimizedMarketBridgeOrder).fillData; + bridgeData = encoder.encode([clipperFillData.poolAddress, NULL_BYTES]); + break; default: throw new Error(AggregationError.NoBridgeForSource); } @@ -475,6 +481,10 @@ export const BRIDGE_ENCODERS: { ]), [ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'), [ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'), + [ERC20BridgeSource.Clipper]: AbiEncoder.create([ + { name: 'provider', type: 'address' }, + { name: 'data', type: 'bytes' }, + ]), }; function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] { diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index c9172b245f..793710b852 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 @@ -20,6 +20,7 @@ import { import { BALANCER_V2_VAULT_ADDRESS_BY_CHAIN, BANCOR_REGISTRY_BY_CHAIN_ID, + CLIPPER_INFO_BY_CHAIN, DODOV1_CONFIG_BY_CHAIN_ID, DODOV2_FACTORIES_BY_CHAIN_ID, KYBER_CONFIG_BY_CHAIN_ID, @@ -359,9 +360,10 @@ export class SamplerOperations { takerToken: string, takerFillAmounts: BigNumber[], gasCost: number, + source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.LiquidityProvider, + source, fillData: { poolAddress: providerAddress, gasCost, @@ -378,9 +380,10 @@ export class SamplerOperations { takerToken: string, makerFillAmounts: BigNumber[], gasCost: number, + source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider, ): SourceQuoteOperation { return new SamplerContractOperation({ - source: ERC20BridgeSource.LiquidityProvider, + source, fillData: { poolAddress: providerAddress, gasCost, @@ -1322,9 +1325,9 @@ export class SamplerOperations { takerToken, makerToken, ) || [] - ).map(poolAddress => + ).map(balancerPool => this.getBalancerSellQuotes( - poolAddress, + balancerPool, makerToken, takerToken, takerFillAmounts, @@ -1358,9 +1361,9 @@ export class SamplerOperations { takerToken, makerToken, ) || [] - ).map(poolAddress => + ).map(creamPool => this.getBalancerSellQuotes( - poolAddress, + creamPool, makerToken, takerToken, takerFillAmounts, @@ -1449,6 +1452,32 @@ export class SamplerOperations { return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts); } + case ERC20BridgeSource.Clipper: + const { poolAddress: clipperPoolAddress, tokens: clipperTokens } = CLIPPER_INFO_BY_CHAIN[ + this.chainId + ]; + if ( + clipperPoolAddress === NULL_ADDRESS || + !clipperTokens.includes(makerToken) || + !clipperTokens.includes(takerToken) + ) { + return []; + } + // Clipper requires WETH to be represented as address(0) + const adjustedMakerToken = + makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; + const adjustedTakerToken = + takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + // Supports the PLP interface + return this.getLiquidityProviderSellQuotes( + clipperPoolAddress, + adjustedMakerToken, + adjustedTakerToken, + takerFillAmounts, + // tslint:disable-next-line: custom-no-magic-numbers + 0, // Not used for Clipper + ERC20BridgeSource.Clipper, + ); default: throw new Error(`Unsupported sell sample source: ${source}`); } @@ -1718,6 +1747,32 @@ export class SamplerOperations { return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts); } + case ERC20BridgeSource.Clipper: + const { poolAddress: clipperPoolAddress, tokens: clipperTokens } = CLIPPER_INFO_BY_CHAIN[ + this.chainId + ]; + if ( + clipperPoolAddress === NULL_ADDRESS || + !clipperTokens.includes(makerToken) || + !clipperTokens.includes(takerToken) + ) { + return []; + } + // Clipper requires WETH to be represented as address(0) + const adjustedMakerToken = + makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken; + const adjustedTakerToken = + takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken; + // Supports the PLP interface + return this.getLiquidityProviderBuyQuotes( + clipperPoolAddress, + adjustedMakerToken, + adjustedTakerToken, + makerFillAmounts, + // tslint:disable-next-line: custom-no-magic-numbers + 0, // Not used for Clipper + ERC20BridgeSource.Clipper, + ); 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 0c52be35d9..33d981a31b 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 { CurveV2 = 'Curve_V2', Lido = 'Lido', ShibaSwap = 'ShibaSwap', + Clipper = 'Clipper', // BSC only PancakeSwap = 'PancakeSwap', PancakeSwapV2 = 'PancakeSwap_V2', diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 02edd2e827..20ea59fd06 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "6.5.0", + "changes": [ + { + "note": "Deployed `Clipper`", + "pr": 299 + } + ] + }, { "version": "6.4.0", "changes": [ diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index eab613cacc..bbbeb20022 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -36,7 +36,7 @@ "wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7", "payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e", "affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f", - "fillQuoteTransformer": "0x6dfdb2537683ce6101f9090a78197dd58c30bfac", + "fillQuoteTransformer": "0xb4fa284689c9784a60d840eb136bb16c5246191f", "positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd" } }, diff --git a/packages/protocol-utils/CHANGELOG.json b/packages/protocol-utils/CHANGELOG.json index 7e8d9fa142..bfccb4006b 100644 --- a/packages/protocol-utils/CHANGELOG.json +++ b/packages/protocol-utils/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "1.8.0", + "changes": [ + { + "note": "Add `Clipper`" + } + ] + }, { "timestamp": 1624356181, "version": "1.7.2", diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index ac16de72b3..bd9d804269 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -131,6 +131,7 @@ export enum BridgeProtocol { KyberDmm, CurveV2, Lido, + Clipper, } // tslint:enable: enum-naming