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:
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "0.23.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added ETH support to `MixinCurve`",
|
||||
"pr": 220
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1619830995,
|
||||
"version": "0.22.3",
|
||||
|
@@ -65,7 +65,7 @@ contract BridgeAdapter is
|
||||
MixinBalancer()
|
||||
MixinBancor(weth)
|
||||
MixinCoFiX()
|
||||
MixinCurve()
|
||||
MixinCurve(weth)
|
||||
MixinCryptoCom()
|
||||
MixinDodo()
|
||||
MixinDodoV2()
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@@ -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 {
|
||||
|
@@ -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(
|
||||
|
@@ -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 } = {
|
||||
|
@@ -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,
|
||||
),
|
||||
|
Reference in New Issue
Block a user