From 891aa8e8bfbb8875631b236e51450e62e040c17c Mon Sep 17 00:00:00 2001 From: Romain Butteaud Date: Tue, 13 Oct 2020 17:06:37 -0700 Subject: [PATCH] feat: SnowSwap --- packages/asset-swapper/src/index.ts | 2 + .../utils/market_operation_utils/constants.ts | 28 ++++++- .../market_operation_utils/curve_utils.ts | 8 +- .../utils/market_operation_utils/orders.ts | 17 ++++ .../sampler_operations.ts | 78 ++++++++++++++++++- .../src/utils/market_operation_utils/types.ts | 8 ++ .../test/market_operation_utils_test.ts | 13 ++++ 7 files changed, 150 insertions(+), 4 deletions(-) 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..28d5bfa1e8 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, @@ -173,7 +175,7 @@ export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = { swUSD: { exchangeFunctionSelector: CurveFunctionSelectors.exchange, sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, - buyQuoteFunctionSelector: CurveFunctionSelectors.get_dx_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, poolAddress: '0x329239599afB305DA0A2eC69c58F8a6697F9F88d', tokens: [ '0x6b175474e89094c44da98b954eedeac495271d0f', @@ -183,6 +185,30 @@ export const MAINNET_SWERVE_INFOS: { [name: string]: CurveInfo } = { ], }, }; +export const MAINNET_SNOWSWAP_INFOS: { [name: string]: CurveInfo } = { + yVaultCurve: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0xBf7CCD6C446acfcc5dF023043f2167B62E81899b', + tokens: [ + '0x5dbcf33d8c2e976c6b560249878e6f1491bca25c', // yUSD + '0x2994529c0652d127b7842094103715ec5299bbed', // ybCRV + ], + }, + yVaultUSD: { + exchangeFunctionSelector: CurveFunctionSelectors.exchange, + sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying, + buyQuoteFunctionSelector: CurveFunctionSelectors.None, + poolAddress: '0x4571753311E37dDb44faA8Fb78a6dF9a6E3c6C0B', + tokens: [ + '0x597ad1e0c13bfe8025993d9e79c69e1c0233522e', // yUSDC + '0xacd43e627e64355f1861cec6d3a6688b31a6f952', // yDAI + '0x2f08119c6f07c006695e079aafc638b8789faf18', // yUSDT + '0x37d19d1c4e1fa9dc47bd1ea12f742a0887eda74a', // yTUSD + ], + }, +}; 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..84bc42df9b 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))); +} \ No newline at end of file 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..efadab35ff 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: @@ -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..a38c97aa00 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, @@ -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, @@ -1041,6 +1099,15 @@ export class SamplerOperations { takerFillAmounts, ), ); + case ERC20BridgeSource.SnowSwap: + return getSnowSwapInfosForPair(takerToken, makerToken).map(snowswap => + this.getSnowSwapSellQuotes( + snowswap, + snowswap.tokens.indexOf(takerToken), + snowswap.tokens.indexOf(makerToken), + takerFillAmounts, + ), + ); case ERC20BridgeSource.LiquidityProvider: if (liquidityProviderRegistryAddress === undefined) { throw new Error( @@ -1164,6 +1231,15 @@ export class SamplerOperations { makerFillAmounts, ), ); + case ERC20BridgeSource.SnowSwap: + return getSnowSwapInfosForPair(takerToken, makerToken).map(snowswap => + this.getSnowSwapBuyQuotes( + snowswap, + snowswap.tokens.indexOf(takerToken), + snowswap.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..0fa88b6142 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 {} @@ -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..80a32aa447 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), @@ -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() },