feat: Support ETH based Curve pools (#220)

* feat: Support ETH based Curve pools

* Disable Curve VIP for WETH trades

* feat: Support for sETH and ankrETH (Curve)

* Disable SnowSwap ETH pools

* feat: add BUSD Curve 3pool

* fix changelog

Co-authored-by: Romain Butteaud <romain.butteaud@gmail.com>
This commit is contained in:
Jacob Evans
2021-05-05 07:33:41 +10:00
committed by GitHub
parent c68b5d7844
commit c00ce9daac
8 changed files with 125 additions and 36 deletions

View File

@@ -1,4 +1,13 @@
[
{
"version": "0.23.0",
"changes": [
{
"note": "Added ETH support to `MixinCurve`",
"pr": 220
}
]
},
{
"timestamp": 1619830995,
"version": "0.22.3",

View File

@@ -65,7 +65,7 @@ contract BridgeAdapter is
MixinBalancer()
MixinBancor(weth)
MixinCoFiX()
MixinCurve()
MixinCurve(weth)
MixinCryptoCom()
MixinDodo()
MixinDodoV2()

View File

@@ -21,6 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
@@ -31,6 +32,15 @@ contract MixinCurve {
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
struct CurveBridgeData {
address curveAddress;
@@ -50,10 +60,17 @@ contract MixinCurve {
{
// Decode the bridge data to get the Curve metadata.
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
sellToken.approveIfBelow(data.curveAddress, sellAmount);
uint256 payableAmount;
if (sellToken == WETH) {
payableAmount = sellAmount;
WETH.withdraw(sellAmount);
} else {
sellToken.approveIfBelow(data.curveAddress, sellAmount);
}
uint256 beforeBalance = buyToken.balanceOf(address(this));
(bool success, bytes memory resultData) =
data.curveAddress.call(abi.encodeWithSelector(
data.curveAddress.call{value: payableAmount}(abi.encodeWithSelector(
data.exchangeFunctionSelector,
data.fromCoinIdx,
data.toCoinIdx,
@@ -65,6 +82,12 @@ contract MixinCurve {
if (!success) {
resultData.rrevert();
}
if (buyToken == WETH) {
boughtAmount = address(this).balance;
WETH.deposit{ value: boughtAmount }();
}
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
}
}

View File

@@ -1,13 +1,4 @@
[
{
"version": "6.10.1",
"changes": [
{
"note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC",
"pr": 222
}
]
},
{
"version": "6.10.0",
"changes": [
@@ -18,6 +9,18 @@
{
"note": "Fix exchangeProxyGasOverhead for fallback path",
"pr": 215
},
{
"note": "Enable ETH based Curve pools",
"pr": 220
},
{
"note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC",
"pr": 222
},
{
"note": "Disable WETH based SnowSwap pools",
"pr": 220
}
]
},

View File

@@ -259,7 +259,11 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
// into WETH prior/post the trade.
// ETH buy/sell is supported
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
) {
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
return {

View File

@@ -233,6 +233,11 @@ export function getShellLikeInfosForPair(
}
}
export interface CurveDetailedInfo extends CurveInfo {
makerTokenIdx: number;
takerTokenIdx: number;
}
export function getCurveLikeInfosForPair(
chainId: ChainId,
takerToken: string,
@@ -247,29 +252,44 @@ export function getCurveLikeInfosForPair(
| ERC20BridgeSource.Smoothy
| ERC20BridgeSource.Saddle
| ERC20BridgeSource.XSigma,
): CurveInfo[] {
): CurveDetailedInfo[] {
let pools: CurveInfo[] = [];
switch (source) {
case ERC20BridgeSource.Curve:
return getCurveInfosForPair(chainId, takerToken, makerToken);
pools = getCurveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Swerve:
return getSwerveInfosForPair(chainId, takerToken, makerToken);
pools = getSwerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.SnowSwap:
return getSnowSwapInfosForPair(chainId, takerToken, makerToken);
pools = getSnowSwapInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Nerve:
return getNerveInfosForPair(chainId, takerToken, makerToken);
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Belt:
return getBeltInfosForPair(chainId, takerToken, makerToken);
pools = getBeltInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Ellipsis:
return getEllipsisInfosForPair(chainId, takerToken, makerToken);
pools = getEllipsisInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Smoothy:
return getSmoothyInfosForPair(chainId, takerToken, makerToken);
pools = getSmoothyInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Saddle:
return getSaddleInfosForPair(chainId, takerToken, makerToken);
pools = getSaddleInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.XSigma:
return getXSigmaInfosForPair(chainId, takerToken, makerToken);
pools = getXSigmaInfosForPair(chainId, takerToken, makerToken);
break;
default:
throw new Error(`Unknown Curve like source ${source}`);
}
return pools.map(pool => ({
...pool,
makerTokenIdx: pool.tokens.indexOf(makerToken),
takerTokenIdx: pool.tokens.indexOf(takerToken),
}));
}
export function uniswapV2LikeRouterAddress(

View File

@@ -332,11 +332,11 @@ export const CURVE_POOLS = {
oBTC: '0xd81da8d904b52208541bade1bd6595d8a251f8dd', // 21.obtc
UST: '0x890f4e345b1daed0367a877a1612f86a1f86985f', // 22.ust
eurs: '0x0ce6a5ff5217e38315f87032cf90686c96627caa', // 23.eurs
// seth: '0xc5424b857f758e906013f3555dad202e4bdb4567', // 24.seth
seth: '0xc5424b857f758e906013f3555dad202e4bdb4567', // 24.seth
aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave
// curve steth: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022' // 26.stETH
steth: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', // 26.stETH
saave: '0xeb16ae0052ed37f479f7fe63849198df1765a733', // saave
// ankreth: '0xa96a65c051bf88b4095ee1f2451c2a9d43f53ae2', // ankreth
ankreth: '0xa96a65c051bf88b4095ee1f2451c2a9d43f53ae2', // ankreth
USDP: '0x42d7025938bec20b69cbae5a77421082407f053a', // usdp
ib: '0x2dded6da1bf5dbdf597c45fcfaa3194e53ecfeaf', // iron bank
link: '0xf178c0b5bb7e7abf4e12a4838c7b7c5ba2c623c0', // link
@@ -346,6 +346,7 @@ export const CURVE_POOLS = {
alUSD: '0x43b4fdfd4ff969587185cdb6f0bd875c5fc83f8c',
FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b',
LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca',
BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a',
};
export const SWERVE_POOLS = {
@@ -355,7 +356,12 @@ export const SWERVE_POOLS = {
export const SNOWSWAP_POOLS = {
yUSD: '0xbf7ccd6c446acfcc5df023043f2167b62e81899b',
yVault: '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b',
eth: '0x16bea2e63adade5984298d53a4d4d9c09e278192',
// POOL Disabled as it uses WETH over ETH
// There is a conflict with Curve and SnowSwap
// where Curve uses ETH and SnowSwap uses WETH
// To re-enable this we need to flag an WETH
// unwrap or not
// eth: '0x16bea2e63adade5984298d53a4d4d9c09e278192',
};
export const SMOOTHY_POOLS = {
@@ -641,6 +647,29 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: CURVE_POOLS.LUSD,
gasSchedule: 387e3,
}),
[CURVE_POOLS.BUSD]: createCurveMetaTriPool({
token: MAINNET_TOKENS.BUSD,
pool: CURVE_POOLS.BUSD,
gasSchedule: 387e3,
}),
[CURVE_POOLS.steth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.stETH],
pool: CURVE_POOLS.steth,
gasSchedule: 151e3,
}),
[CURVE_POOLS.seth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.sETH],
pool: CURVE_POOLS.seth,
gasSchedule: 187e3,
}),
[CURVE_POOLS.ankreth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.ankrETH],
pool: CURVE_POOLS.ankreth,
gasSchedule: 125e3,
}),
};
export const SWERVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
@@ -667,11 +696,12 @@ export const SNOWSWAP_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: SNOWSWAP_POOLS.yVault,
gasSchedule: 1490e3,
}),
[SNOWSWAP_POOLS.eth]: createCurveExchangePool({
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.vETH, MAINNET_TOKENS.ankrETH, MAINNET_TOKENS.crETH],
pool: SNOWSWAP_POOLS.eth,
gasSchedule: 990e3,
}),
// Unsupported due to collision with WETH and ETH with execution using MixinCurve
// [SNOWSWAP_POOLS.eth]: createCurveExchangePool({
// tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.vETH, MAINNET_TOKENS.ankrETH, MAINNET_TOKENS.crETH],
// pool: SNOWSWAP_POOLS.eth,
// gasSchedule: 990e3,
// }),
};
export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = {

View File

@@ -1136,8 +1136,8 @@ export class SamplerOperations {
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveSellQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
pool.takerTokenIdx,
pool.makerTokenIdx,
takerFillAmounts,
source,
),
@@ -1351,8 +1351,8 @@ export class SamplerOperations {
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveBuyQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
pool.takerTokenIdx,
pool.makerTokenIdx,
makerFillAmounts,
source,
),