diff --git a/packages/asset-swapper/contracts/src/CurveSampler.sol b/packages/asset-swapper/contracts/src/CurveSampler.sol index 62e4d611e0..36b1de99db 100644 --- a/packages/asset-swapper/contracts/src/CurveSampler.sol +++ b/packages/asset-swapper/contracts/src/CurveSampler.sol @@ -37,7 +37,7 @@ contract CurveSampler is /// @dev Base gas limit for Curve calls. Some Curves have multiple tokens /// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens. - uint256 constant private CURVE_CALL_GAS = 600e3; // 600k + uint256 constant private CURVE_CALL_GAS = 2000e3; // Was 600k for Curve but SnowSwap is using 1500k+ /// @dev Sample sell quotes from Curve. /// @param curveInfo Curve information specific to this token pair. diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 0c87279cc8..21fcc1b3b9 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -155,6 +155,8 @@ export { SushiSwapFillData, SwerveFillData, SwerveInfo, + SnowSwapFillData, + SnowSwapInfo, TokenAdjacencyGraph, UniswapV2FillData, } from './utils/market_operation_utils/types'; 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 1bb895690c..b17aaa5b71 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -21,6 +21,7 @@ export const SELL_SOURCE_FILTER = new SourceFilters([ ERC20BridgeSource.MStable, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.Swerve, + ERC20BridgeSource.SnowSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.Shell, ERC20BridgeSource.MultiHop, @@ -45,6 +46,7 @@ export const BUY_SOURCE_FILTER = new SourceFilters( ERC20BridgeSource.Mooniswap, ERC20BridgeSource.Shell, ERC20BridgeSource.Swerve, + ERC20BridgeSource.SnowSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Dodo, @@ -172,9 +174,9 @@ export const MAINNET_CURVE_INFOS: { [name: string]: CurveInfo } = { export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = { swUSD: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, - sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, - poolAddress: '0x329239599afB305DA0A2eC69c58F8a6697F9F88d', + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0x329239599afb305da0a2ec69c58f8a6697f9f88d', // _target: 0xa5407eae9ba41422680e2e00537571bcc53efbfd tokens: [ '0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', @@ -183,6 +185,53 @@ export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = { ], }, }; +export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = { + yVaultCurve: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, + buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx, + poolAddress: '0xbf7ccd6c446acfcc5df023043f2167b62e81899b', + tokens: [ + '0x5dbcf33d8c2e976c6b560249878e6f1491bca25c', // yUSD + '0x2994529c0652d127b7842094103715ec5299bbed', // ybCRV + ], + }, + yVaultCurveUnderlying: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, + poolAddress: '0xbf7ccd6c446acfcc5df023043f2167b62e81899b', + tokens: [ + '0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8', // yCRV + '0x3b3ac5386837dc563660fb6a0937dfaa5924333b', // bCRV + ], + }, + yVaultUSD: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy, + buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx, + poolAddress: '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b', + tokens: [ + '0xacd43e627e64355f1861cec6d3a6688b31a6f952', // yDAI + '0x597ad1e0c13bfe8025993d9e79c69e1c0233522e', // yUSDC + '0x2f08119c6f07c006695e079aafc638b8789faf18', // yUSDT + '0x37d19d1c4e1fa9dc47bd1ea12f742a0887eda74a', // yTUSD + ], + }, + // Gas is too high for these underlying tokens (3M+) + // yVaultUSDUnderlying: { + // exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying, + // sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + // buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, + // poolAddress: '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b', + // tokens: [ + // '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + // '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + // '0x0000000000085d4780b73119b644ae5ecd22b376', // TUSD + // ], + // }, +}; export const MAINNET_KYBER_RESERVE_IDS: { [name: string]: string } = { Reserve1: '0xff4b796265722046707200000000000000000000000000000000000000000000', diff --git a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts index e1b70ab492..f84dc86c15 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts @@ -1,5 +1,5 @@ -import { MAINNET_CURVE_INFOS, MAINNET_SWERVE_INFOS } from './constants'; -import { CurveInfo, SwerveInfo } from './types'; +import { MAINNET_CURVE_INFOS, MAINNET_SNOWSWAP_INFOS, MAINNET_SWERVE_INFOS } from './constants'; +import { CurveInfo, SnowSwapInfo, SwerveInfo } from './types'; // tslint:disable completed-docs export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] { @@ -9,3 +9,7 @@ export function getCurveInfosForPair(takerToken: string, makerToken: string): Cu export function getSwerveInfosForPair(takerToken: string, makerToken: string): SwerveInfo[] { return Object.values(MAINNET_SWERVE_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))); } + +export function getSnowSwapInfosForPair(takerToken: string, makerToken: string): SnowSwapInfo[] { + return Object.values(MAINNET_SNOWSWAP_INFOS).filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t))); +} 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 e06b815718..d78c066e1a 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -34,6 +34,7 @@ import { NativeCollapsedFill, OptimizedMarketOrder, OrderDomain, + SnowSwapFillData, SushiSwapFillData, SwerveFillData, UniswapV2FillData, @@ -173,6 +174,8 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath return opts.contractAddresses.curveBridge; case ERC20BridgeSource.Swerve: return opts.contractAddresses.curveBridge; + case ERC20BridgeSource.SnowSwap: + return opts.contractAddresses.curveBridge; case ERC20BridgeSource.Bancor: return opts.contractAddresses.bancorBridge; case ERC20BridgeSource.Balancer: @@ -213,8 +216,8 @@ export function createBridgeOrder( makerToken, bridgeAddress, createCurveBridgeData( - curveFillData.curve.poolAddress, - curveFillData.curve.exchangeFunctionSelector, + curveFillData.pool.poolAddress, + curveFillData.pool.exchangeFunctionSelector, takerToken, curveFillData.fromTokenIdx, curveFillData.toTokenIdx, @@ -235,6 +238,20 @@ export function createBridgeOrder( ), ); break; + case ERC20BridgeSource.SnowSwap: + const snowSwapFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createCurveBridgeData( + snowSwapFillData.pool.poolAddress, + snowSwapFillData.pool.exchangeFunctionSelector, + takerToken, + snowSwapFillData.fromTokenIdx, + snowSwapFillData.toTokenIdx, + ), + ); + break; case ERC20BridgeSource.Balancer: const balancerFillData = (fill as CollapsedFill).fillData!; // tslint:disable-line:no-non-null-assertion makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( 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 5d5b0385a8..583a3965f4 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 @@ -9,7 +9,7 @@ import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } import { BancorService } from './bancor_service'; import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; import { CreamPoolsCache } from './cream_utils'; -import { getCurveInfosForPair, getSwerveInfosForPair } from './curve_utils'; +import { getCurveInfosForPair, getSnowSwapInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getKyberReserveIdsForPair } from './kyber_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; import { getIntermediateTokens } from './multihop_utils'; @@ -30,6 +30,8 @@ import { MooniswapFillData, MultiBridgeFillData, MultiHopFillData, + SnowSwapFillData, + SnowSwapInfo, SourceQuoteOperation, SushiSwapFillData, SwerveFillData, @@ -296,7 +298,7 @@ export class SamplerOperations { } public getCurveSellQuotes( - curve: CurveInfo, + pool: CurveInfo, fromTokenIdx: number, toTokenIdx: number, takerFillAmounts: BigNumber[], @@ -304,7 +306,7 @@ export class SamplerOperations { return new SamplerContractOperation({ source: ERC20BridgeSource.Curve, fillData: { - curve, + pool, fromTokenIdx, toTokenIdx, }, @@ -312,9 +314,9 @@ export class SamplerOperations { function: this._samplerContract.sampleSellsFromCurve, params: [ { - poolAddress: curve.poolAddress, - sellQuoteFunctionSelector: curve.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: curve.buyQuoteFunctionSelector, + poolAddress: pool.poolAddress, + sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, + buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, }, new BigNumber(fromTokenIdx), new BigNumber(toTokenIdx), @@ -324,7 +326,7 @@ export class SamplerOperations { } public getCurveBuyQuotes( - curve: CurveInfo, + pool: CurveInfo, fromTokenIdx: number, toTokenIdx: number, makerFillAmounts: BigNumber[], @@ -332,7 +334,7 @@ export class SamplerOperations { return new SamplerContractOperation({ source: ERC20BridgeSource.Curve, fillData: { - curve, + pool, fromTokenIdx, toTokenIdx, }, @@ -340,9 +342,9 @@ export class SamplerOperations { function: this._samplerContract.sampleBuysFromCurve, params: [ { - poolAddress: curve.poolAddress, - sellQuoteFunctionSelector: curve.sellQuoteFunctionSelector, - buyQuoteFunctionSelector: curve.buyQuoteFunctionSelector, + poolAddress: pool.poolAddress, + sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, + buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, }, new BigNumber(fromTokenIdx), new BigNumber(toTokenIdx), @@ -407,6 +409,62 @@ export class SamplerOperations { }); } + public getSnowSwapSellQuotes( + pool: SnowSwapInfo, + fromTokenIdx: number, + toTokenIdx: number, + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.SnowSwap, + fillData: { + pool, + fromTokenIdx, + toTokenIdx, + }, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromCurve, + params: [ + { + poolAddress: pool.poolAddress, + sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, + buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, + }, + new BigNumber(fromTokenIdx), + new BigNumber(toTokenIdx), + takerFillAmounts, + ], + }); + } + + public getSnowSwapBuyQuotes( + pool: SnowSwapInfo, + fromTokenIdx: number, + toTokenIdx: number, + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.SnowSwap, + fillData: { + pool, + fromTokenIdx, + toTokenIdx, + }, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromCurve, + params: [ + { + poolAddress: pool.poolAddress, + sellQuoteFunctionSelector: pool.sellQuoteFunctionSelector, + buyQuoteFunctionSelector: pool.buyQuoteFunctionSelector, + }, + new BigNumber(fromTokenIdx), + new BigNumber(toTokenIdx), + makerFillAmounts, + ], + }); + } + public getBalancerSellQuotes( poolAddress: string, makerToken: string, @@ -1024,11 +1082,11 @@ export class SamplerOperations { this.getKyberSellQuotes(reserveId, makerToken, takerToken, takerFillAmounts), ); case ERC20BridgeSource.Curve: - return getCurveInfosForPair(takerToken, makerToken).map(curve => + return getCurveInfosForPair(takerToken, makerToken).map(pool => this.getCurveSellQuotes( - curve, - curve.tokens.indexOf(takerToken), - curve.tokens.indexOf(makerToken), + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), takerFillAmounts, ), ); @@ -1041,6 +1099,15 @@ export class SamplerOperations { takerFillAmounts, ), ); + case ERC20BridgeSource.SnowSwap: + return getSnowSwapInfosForPair(takerToken, makerToken).map(pool => + this.getSnowSwapSellQuotes( + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), + takerFillAmounts, + ), + ); case ERC20BridgeSource.LiquidityProvider: if (liquidityProviderRegistryAddress === undefined) { throw new Error( @@ -1147,11 +1214,11 @@ export class SamplerOperations { this.getKyberBuyQuotes(reserveId, makerToken, takerToken, makerFillAmounts), ); case ERC20BridgeSource.Curve: - return getCurveInfosForPair(takerToken, makerToken).map(curve => + return getCurveInfosForPair(takerToken, makerToken).map(pool => this.getCurveBuyQuotes( - curve, - curve.tokens.indexOf(takerToken), - curve.tokens.indexOf(makerToken), + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), makerFillAmounts, ), ); @@ -1164,6 +1231,15 @@ export class SamplerOperations { makerFillAmounts, ), ); + case ERC20BridgeSource.SnowSwap: + return getSnowSwapInfosForPair(takerToken, makerToken).map(pool => + this.getSnowSwapBuyQuotes( + pool, + pool.tokens.indexOf(takerToken), + pool.tokens.indexOf(makerToken), + makerFillAmounts, + ), + ); case ERC20BridgeSource.LiquidityProvider: if (liquidityProviderRegistryAddress === undefined) { throw new Error( 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 5a7ebc9dc4..601eac2936 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -44,6 +44,7 @@ export enum ERC20BridgeSource { MultiHop = 'MultiHop', Shell = 'Shell', Swerve = 'Swerve', + SnowSwap = 'SnowSwap', SushiSwap = 'SushiSwap', Dodo = 'DODO', } @@ -75,6 +76,7 @@ export interface CurveInfo { } export interface SwerveInfo extends CurveInfo {} +export interface SnowSwapInfo extends CurveInfo {} // Internal `fillData` field for `Fill` objects. export interface FillData {} @@ -92,7 +94,7 @@ export interface NativeFillData extends FillData { export interface CurveFillData extends FillData { fromTokenIdx: number; toTokenIdx: number; - curve: CurveInfo; + pool: CurveInfo; } export interface SwerveFillData extends FillData { @@ -101,6 +103,12 @@ export interface SwerveFillData extends FillData { pool: SwerveInfo; } +export interface SnowSwapFillData extends FillData { + fromTokenIdx: number; + toTokenIdx: number; + pool: SnowSwapInfo; +} + export interface BalancerFillData extends FillData { poolAddress: string; } diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 8e54b748fa..3628329c81 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -43,6 +43,7 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.Mooniswap, ERC20BridgeSource.Bancor, ERC20BridgeSource.Swerve, + ERC20BridgeSource.SnowSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Shell, @@ -284,6 +285,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.SnowSwap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MultiHop]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Shell]: _.times(NUM_SAMPLES, () => 0), @@ -308,7 +310,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() }, [ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x' }, [ERC20BridgeSource.Curve]: { - curve: { + pool: { poolAddress: randomAddress(), tokens: [TAKER_TOKEN, MAKER_TOKEN], exchangeFunctionSelector: hexUtils.random(4), @@ -329,6 +331,17 @@ describe('MarketOperationUtils tests', () => { fromTokenIdx: 0, toTokenIdx: 1, }, + [ERC20BridgeSource.SnowSwap]: { + pool: { + poolAddress: randomAddress(), + tokens: [TAKER_TOKEN, MAKER_TOKEN], + exchangeFunctionSelector: hexUtils.random(4), + sellQuoteFunctionSelector: hexUtils.random(4), + buyQuoteFunctionSelector: hexUtils.random(4), + }, + fromTokenIdx: 0, + toTokenIdx: 1, + }, [ERC20BridgeSource.LiquidityProvider]: { poolAddress: randomAddress() }, [ERC20BridgeSource.SushiSwap]: { tokenAddressPath: [] }, [ERC20BridgeSource.Mooniswap]: { poolAddress: randomAddress() },