feat: Clipper (#299)

* feat: Clipper

* feat: Curve tricrypto2 (#302)

* Scope down the token list search space for Clipper

* update deployed addresses
This commit is contained in:
Jacob Evans 2021-08-06 14:12:43 +10:00 committed by GitHub
parent 59eabec71e
commit a35af11981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 310 additions and 9 deletions

View File

@ -1,4 +1,12 @@
[ [
{
"version": "0.27.0",
"changes": [
{
"note": "Add `Clipper` as a custom liquidity source"
}
]
},
{ {
"version": "0.26.0", "version": "0.26.0",
"changes": [ "changes": [

View File

@ -25,6 +25,7 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinBalancer.sol"; import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol"; import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol"; import "./mixins/MixinBancor.sol";
import "./mixins/MixinClipper.sol";
import "./mixins/MixinCoFiX.sol"; import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCurve.sol"; import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol"; import "./mixins/MixinCurveV2.sol";
@ -50,6 +51,7 @@ contract BridgeAdapter is
MixinBalancer, MixinBalancer,
MixinBalancerV2, MixinBalancerV2,
MixinBancor, MixinBancor,
MixinClipper,
MixinCoFiX, MixinCoFiX,
MixinCurve, MixinCurve,
MixinCurveV2, MixinCurveV2,
@ -75,6 +77,7 @@ contract BridgeAdapter is
MixinBalancer() MixinBalancer()
MixinBalancerV2() MixinBalancerV2()
MixinBancor(weth) MixinBancor(weth)
MixinClipper(weth)
MixinCoFiX() MixinCoFiX()
MixinCurve(weth) MixinCurve(weth)
MixinCurveV2() MixinCurveV2()
@ -245,6 +248,13 @@ contract BridgeAdapter is
sellAmount, sellAmount,
order.bridgeData order.bridgeData
); );
} else if (protocolId == BridgeProtocols.CLIPPER) {
boughtAmount = _tradeClipper(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else { } else {
boughtAmount = _tradeZeroExBridge( boughtAmount = _tradeZeroExBridge(
sellToken, sellToken,

View File

@ -49,4 +49,5 @@ library BridgeProtocols {
uint128 internal constant KYBERDMM = 19; uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20; uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21; uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22;
} }

View File

@ -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
);
}
}

View File

@ -43,7 +43,7 @@
"config": { "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", "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: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": { "repository": {
"type": "git", "type": "git",

View File

@ -82,6 +82,7 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json'; import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json'; import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.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 MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json'; import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json'; import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
@ -257,6 +258,7 @@ export const artifacts = {
MixinBalancer: MixinBalancer as ContractArtifact, MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact, MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact, MixinBancor: MixinBancor as ContractArtifact,
MixinClipper: MixinClipper as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact, MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact, MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact, MixinCurve: MixinCurve as ContractArtifact,

View File

@ -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';
export * from '../test/generated-wrappers/mixin_balancer_v2'; export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_bancor'; 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_co_fi_x';
export * from '../test/generated-wrappers/mixin_crypto_com'; export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve'; export * from '../test/generated-wrappers/mixin_curve';

View File

@ -113,6 +113,7 @@
"test/generated-artifacts/MixinBalancer.json", "test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json", "test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json", "test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinClipper.json",
"test/generated-artifacts/MixinCoFiX.json", "test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCryptoCom.json", "test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json", "test/generated-artifacts/MixinCurve.json",

View File

@ -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", "version": "16.23.1",
"changes": [ "changes": [

View File

@ -98,6 +98,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3, ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap, ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Clipper,
]), ]),
[ChainId.Ropsten]: new SourceFilters([ [ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber, ERC20BridgeSource.Kyber,
@ -198,6 +199,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3, ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2, ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap, ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Clipper,
]), ]),
[ChainId.Ropsten]: new SourceFilters([ [ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber, ERC20BridgeSource.Kyber,
@ -373,6 +375,8 @@ export const MAINNET_TOKENS = {
FRAX: '0x853d955acef822db058eb8505911ed77f175b99e', FRAX: '0x853d955acef822db058eb8505911ed77f175b99e',
LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0', LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0',
FEI: '0x956f47f50a910163d8bf957cf5846d573e7f87ca', FEI: '0x956f47f50a910163d8bf957cf5846d573e7f87ca',
DSU: '0x605d26fbd5be761089281d5cec2ce86eea667109',
ESS: '0x24ae124c4cc33d6791f8e8b63520ed7107ac8b3e',
}; };
export const BSC_TOKENS = { export const BSC_TOKENS = {
@ -448,10 +452,12 @@ export const CURVE_POOLS = {
FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b', FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b',
LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca', LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca',
BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a', BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a',
DSU3CRV: '0x6ec80df362d7042c50d4469bcfbc174c9dd9109a',
}; };
export const CURVE_V2_POOLS = { export const CURVE_V2_POOLS = {
tricrypto: '0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5', tricrypto: '0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5',
tricrypto2: '0xd51a44d3fae010294c616388b506acda1bfaae46',
}; };
export const CURVE_POLYGON_POOLS = { export const CURVE_POLYGON_POOLS = {
@ -847,6 +853,11 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: CURVE_POOLS.ankreth, pool: CURVE_POOLS.ankreth,
gasSchedule: 125e3, 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 } = { 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, pool: CURVE_V2_POOLS.tricrypto,
gasSchedule: 300e3, 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 } = { export const CURVE_POLYGON_INFOS: { [name: string]: CurveInfo } = {
@ -1410,6 +1426,22 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId<LidoInfo>(
}, },
); );
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_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3; export const BALANCER_MAX_POOLS_FETCHED = 3;
@ -1640,6 +1672,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
return gas; return gas;
}, },
[ERC20BridgeSource.Lido]: () => 226e3, [ERC20BridgeSource.Lido]: () => 226e3,
[ERC20BridgeSource.Clipper]: () => 170e3,
// //
// BSC // BSC

View File

@ -3,7 +3,7 @@ import { AbiEncoder, BigNumber } from '@0x/utils';
import { AssetSwapperContractAddresses, MarketOperation } from '../../types'; import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
import { MAX_UINT256, ZERO_AMOUNT } from './constants'; import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants';
import { import {
AggregationError, AggregationError,
BalancerFillData, BalancerFillData,
@ -180,6 +180,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap'); return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap');
case ERC20BridgeSource.ACryptos: case ERC20BridgeSource.ACryptos:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'ACryptoS'); return encodeBridgeSourceId(BridgeProtocol.Curve, 'ACryptoS');
case ERC20BridgeSource.Clipper:
return encodeBridgeSourceId(BridgeProtocol.Clipper, 'Clipper');
default: default:
throw new Error(AggregationError.NoBridgeForSource); throw new Error(AggregationError.NoBridgeForSource);
} }
@ -318,6 +320,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData; const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]); bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
break; break;
case ERC20BridgeSource.Clipper:
const clipperFillData = (order as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
bridgeData = encoder.encode([clipperFillData.poolAddress, NULL_BYTES]);
break;
default: default:
throw new Error(AggregationError.NoBridgeForSource); throw new Error(AggregationError.NoBridgeForSource);
} }
@ -475,6 +481,10 @@ export const BRIDGE_ENCODERS: {
]), ]),
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'), [ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
[ERC20BridgeSource.Lido]: AbiEncoder.create('(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] { function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@ -20,6 +20,7 @@ import {
import { import {
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN, BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID, BANCOR_REGISTRY_BY_CHAIN_ID,
CLIPPER_INFO_BY_CHAIN,
DODOV1_CONFIG_BY_CHAIN_ID, DODOV1_CONFIG_BY_CHAIN_ID,
DODOV2_FACTORIES_BY_CHAIN_ID, DODOV2_FACTORIES_BY_CHAIN_ID,
KYBER_CONFIG_BY_CHAIN_ID, KYBER_CONFIG_BY_CHAIN_ID,
@ -359,9 +360,10 @@ export class SamplerOperations {
takerToken: string, takerToken: string,
takerFillAmounts: BigNumber[], takerFillAmounts: BigNumber[],
gasCost: number, gasCost: number,
source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider,
): SourceQuoteOperation<LiquidityProviderFillData> { ): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider, source,
fillData: { fillData: {
poolAddress: providerAddress, poolAddress: providerAddress,
gasCost, gasCost,
@ -378,9 +380,10 @@ export class SamplerOperations {
takerToken: string, takerToken: string,
makerFillAmounts: BigNumber[], makerFillAmounts: BigNumber[],
gasCost: number, gasCost: number,
source: ERC20BridgeSource = ERC20BridgeSource.LiquidityProvider,
): SourceQuoteOperation<LiquidityProviderFillData> { ): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider, source,
fillData: { fillData: {
poolAddress: providerAddress, poolAddress: providerAddress,
gasCost, gasCost,
@ -1322,9 +1325,9 @@ export class SamplerOperations {
takerToken, takerToken,
makerToken, makerToken,
) || [] ) || []
).map(poolAddress => ).map(balancerPool =>
this.getBalancerSellQuotes( this.getBalancerSellQuotes(
poolAddress, balancerPool,
makerToken, makerToken,
takerToken, takerToken,
takerFillAmounts, takerFillAmounts,
@ -1358,9 +1361,9 @@ export class SamplerOperations {
takerToken, takerToken,
makerToken, makerToken,
) || [] ) || []
).map(poolAddress => ).map(creamPool =>
this.getBalancerSellQuotes( this.getBalancerSellQuotes(
poolAddress, creamPool,
makerToken, makerToken,
takerToken, takerToken,
takerFillAmounts, takerFillAmounts,
@ -1449,6 +1452,32 @@ export class SamplerOperations {
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts); 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: default:
throw new Error(`Unsupported sell sample source: ${source}`); throw new Error(`Unsupported sell sample source: ${source}`);
} }
@ -1718,6 +1747,32 @@ export class SamplerOperations {
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts); 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: default:
throw new Error(`Unsupported buy sample source: ${source}`); throw new Error(`Unsupported buy sample source: ${source}`);
} }

View File

@ -69,6 +69,7 @@ export enum ERC20BridgeSource {
CurveV2 = 'Curve_V2', CurveV2 = 'Curve_V2',
Lido = 'Lido', Lido = 'Lido',
ShibaSwap = 'ShibaSwap', ShibaSwap = 'ShibaSwap',
Clipper = 'Clipper',
// BSC only // BSC only
PancakeSwap = 'PancakeSwap', PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2', PancakeSwapV2 = 'PancakeSwap_V2',

View File

@ -1,4 +1,13 @@
[ [
{
"version": "6.5.0",
"changes": [
{
"note": "Deployed `Clipper`",
"pr": 299
}
]
},
{ {
"version": "6.4.0", "version": "6.4.0",
"changes": [ "changes": [

View File

@ -36,7 +36,7 @@
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7", "wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
"payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e", "payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e",
"affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f", "affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f",
"fillQuoteTransformer": "0x6dfdb2537683ce6101f9090a78197dd58c30bfac", "fillQuoteTransformer": "0xb4fa284689c9784a60d840eb136bb16c5246191f",
"positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd" "positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd"
} }
}, },

View File

@ -1,4 +1,12 @@
[ [
{
"version": "1.8.0",
"changes": [
{
"note": "Add `Clipper`"
}
]
},
{ {
"timestamp": 1624356181, "timestamp": 1624356181,
"version": "1.7.2", "version": "1.7.2",

View File

@ -131,6 +131,7 @@ export enum BridgeProtocol {
KyberDmm, KyberDmm,
CurveV2, CurveV2,
Lido, Lido,
Clipper,
} }
// tslint:enable: enum-naming // tslint:enable: enum-naming