diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index c34151262e..ff0e21a015 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -649,7 +649,7 @@ contract ERC20BridgeSampler is } /// @dev Sample sell quotes from UniswapV2. - /// @param path Token route. + /// @param path Token route. Should be takerToken -> makerToken /// @param takerTokenAmounts Taker token sell amount for each sample. /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. @@ -683,7 +683,7 @@ contract ERC20BridgeSampler is } /// @dev Sample buy quotes from UniswapV2. - /// @param path Token route. + /// @param path Token route. Should be takerToken -> makerToken. /// @param makerTokenAmounts Maker token buy amount for each sample. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 4e8af75f3d..d5dcba1d85 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -77,6 +77,10 @@ { "note": "Increase timeout for tests", "pr": 2587 + }, + { + "note": "Add support for Uniswap V2", + "pr": 2599 } ] }, 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 0800c90fb6..346fbc85a7 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -9,6 +9,8 @@ import { ERC20BridgeSource, FakeBuyOpts, GetMarketOrdersOpts } from './types'; */ export const SELL_SOURCES = [ ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.UniswapV2Eth, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber, // All Curve Sources @@ -24,6 +26,8 @@ export const SELL_SOURCES = [ */ export const BUY_SOURCES = [ ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ERC20BridgeSource.UniswapV2Eth, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber, // All Curve sources diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts index 1bc2d443f0..c7031351a8 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -93,6 +93,7 @@ export class MarketOperationUtils { makerToken, this._wethAddress, ONE_ETHER, + this._wethAddress, this._liquidityProviderRegistry, ), // Get sell quotes for taker -> maker. @@ -103,6 +104,7 @@ export class MarketOperationUtils { makerToken, takerToken, getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase), + this._wethAddress, this._liquidityProviderRegistry, ), ); @@ -173,6 +175,7 @@ export class MarketOperationUtils { takerToken, this._wethAddress, ONE_ETHER, + this._wethAddress, this._liquidityProviderRegistry, ), // Get buy quotes for taker -> maker. @@ -183,6 +186,7 @@ export class MarketOperationUtils { makerToken, takerToken, getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase), + this._wethAddress, this._liquidityProviderRegistry, ), ); @@ -248,6 +252,7 @@ export class MarketOperationUtils { getNativeOrderTokens(orders[0])[1], this._wethAddress, ONE_ETHER, + this._wethAddress, ), ), ...batchNativeOrders.map((orders, i) => @@ -256,6 +261,7 @@ export class MarketOperationUtils { getNativeOrderTokens(orders[0])[0], getNativeOrderTokens(orders[0])[1], [makerAmounts[i]], + this._wethAddress, ), ), ]; 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 3cb3f70539..32518e72d8 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -5,7 +5,7 @@ import { AbiEncoder, BigNumber } from '@0x/utils'; import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types'; import { RfqtIndicativeQuoteResponse } from '../quote_requestor'; -import { getCurveInfo, isCurveSource } from '../source_utils'; +import { getCurveInfo } from '../source_utils'; import { ERC20_PROXY_ID, @@ -181,6 +181,9 @@ function getBridgeAddressFromSource(source: ERC20BridgeSource, opts: CreateOrder return opts.contractAddresses.kyberBridge; case ERC20BridgeSource.Uniswap: return opts.contractAddresses.uniswapBridge; + case ERC20BridgeSource.UniswapV2: + case ERC20BridgeSource.UniswapV2Eth: + return opts.contractAddresses.uniswapV2Bridge; case ERC20BridgeSource.CurveUsdcDai: case ERC20BridgeSource.CurveUsdcDaiUsdt: case ERC20BridgeSource.CurveUsdcDaiUsdtTusd: @@ -203,19 +206,48 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): const bridgeAddress = getBridgeAddressFromSource(fill.source, opts); let makerAssetData; - if (isCurveSource(fill.source)) { - const { curveAddress, fromTokenIdx, toTokenIdx, version } = getCurveInfo(fill.source, takerToken, makerToken); - makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( - makerToken, - bridgeAddress, - createCurveBridgeData(curveAddress, fromTokenIdx, toTokenIdx, version), - ); - } else { - makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( - makerToken, - bridgeAddress, - createBridgeData(takerToken), - ); + switch (fill.source) { + case ERC20BridgeSource.CurveUsdcDai: + case ERC20BridgeSource.CurveUsdcDaiUsdt: + case ERC20BridgeSource.CurveUsdcDaiUsdtTusd: + case ERC20BridgeSource.CurveUsdcDaiUsdtBusd: + case ERC20BridgeSource.CurveUsdcDaiUsdtSusd: + const { curveAddress, fromTokenIdx, toTokenIdx, version } = getCurveInfo( + fill.source, + takerToken, + makerToken, + ); + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createCurveBridgeData(curveAddress, fromTokenIdx, toTokenIdx, version), + ); + break; + case ERC20BridgeSource.UniswapV2: + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createUniswapV2BridgeData([makerToken, takerToken]), + ); + break; + case ERC20BridgeSource.UniswapV2Eth: + if (opts.contractAddresses.etherToken === NULL_ADDRESS) { + throw new Error( + `Cannot create a ${ERC20BridgeSource.UniswapV2Eth.toString()} order without a WETH address`, + ); + } + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createUniswapV2BridgeData([makerToken, opts.contractAddresses.etherToken, takerToken]), + ); + break; + default: + makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( + makerToken, + bridgeAddress, + createBridgeData(takerToken), + ); } const [slippedMakerAssetAmount, slippedTakerAssetAmount] = getSlippedBridgeAssetAmounts(fill, opts); return { @@ -298,6 +330,11 @@ function createCurveBridgeData( return curveBridgeDataEncoder.encode([curveAddress, fromTokenIdx, toTokenIdx, version]); } +function createUniswapV2BridgeData(tokenAddressPath: string[]): string { + const uniswapV2BridgeDataEncoder = AbiEncoder.create('(address[])'); + return uniswapV2BridgeDataEncoder.encode([tokenAddressPath]); +} + function getSlippedBridgeAssetAmounts(fill: CollapsedFill, opts: CreateOrderFromPathOpts): [BigNumber, BigNumber] { return [ // Maker asset amount. 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 e3ffb3d57c..bb7acfbf7a 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 @@ -98,6 +98,30 @@ export const samplerOperations = { }, }; }, + getUniswapV2SellQuotes(tokenAddressPath: string[], takerFillAmounts: BigNumber[]): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleSellsFromUniswapV2(tokenAddressPath, takerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleSellsFromUniswapV2', callResults); + }, + }; + }, + getUniswapV2BuyQuotes(tokenAddressPath: string[], makerFillAmounts: BigNumber[]): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleBuysFromUniswapV2(tokenAddressPath, makerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleBuysFromUniswapV2', callResults); + }, + }; + }, getLiquidityProviderSellQuotes( liquidityProviderRegistryAddress: string, makerToken: string, @@ -231,6 +255,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, takerFillAmount: BigNumber, + wethAddress: string, liquidityProviderRegistryAddress?: string | undefined, ): BatchedOperation { if (makerToken.toLowerCase() === takerToken.toLowerCase()) { @@ -241,6 +266,7 @@ export const samplerOperations = { makerToken, takerToken, [takerFillAmount], + wethAddress, liquidityProviderRegistryAddress, ); return { @@ -297,6 +323,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, takerFillAmounts: BigNumber[], + wethAddress: string, liquidityProviderRegistryAddress?: string | undefined, ): BatchedOperation { const subOps = sources @@ -306,6 +333,16 @@ export const samplerOperations = { batchedOperation = samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts); } else if (source === ERC20BridgeSource.Uniswap) { batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts); + } else if (source === ERC20BridgeSource.UniswapV2) { + batchedOperation = samplerOperations.getUniswapV2SellQuotes( + [takerToken, makerToken], + takerFillAmounts, + ); + } else if (source === ERC20BridgeSource.UniswapV2Eth) { + batchedOperation = samplerOperations.getUniswapV2SellQuotes( + [takerToken, wethAddress, makerToken], + takerFillAmounts, + ); } else if (source === ERC20BridgeSource.Kyber) { batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts); } else if (isCurveSource(source)) { @@ -366,6 +403,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], + wethAddress: string, liquidityProviderRegistryAddress?: string | undefined, fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, ): BatchedOperation { @@ -376,6 +414,16 @@ export const samplerOperations = { batchedOperation = samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); } else if (source === ERC20BridgeSource.Uniswap) { batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); + } else if (source === ERC20BridgeSource.UniswapV2) { + batchedOperation = samplerOperations.getUniswapV2BuyQuotes( + [takerToken, makerToken], + makerFillAmounts, + ); + } else if (source === ERC20BridgeSource.UniswapV2Eth) { + batchedOperation = samplerOperations.getUniswapV2BuyQuotes( + [takerToken, wethAddress, makerToken], + makerFillAmounts, + ); } else if (source === ERC20BridgeSource.Kyber) { batchedOperation = samplerOperations.getKyberBuyQuotes( makerToken, 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 5f85b85891..a4fad71983 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -28,6 +28,8 @@ export enum AggregationError { export enum ERC20BridgeSource { Native = 'Native', Uniswap = 'Uniswap', + UniswapV2 = 'Uniswap_V2', + UniswapV2Eth = 'Uniswap_V2_ETH', Eth2Dai = 'Eth2Dai', Kyber = 'Kyber', CurveUsdcDai = 'Curve_USDC_DAI', diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts index dc9d174215..4b11767461 100644 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ b/packages/asset-swapper/test/dex_sampler_test.ts @@ -1,3 +1,4 @@ +import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses'; import { constants, expect, @@ -24,6 +25,8 @@ describe('DexSampler tests', () => { const MAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(MAKER_TOKEN); const TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN); + const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken; + describe('getSampleAmounts()', () => { const FILL_AMOUNT = getRandomInteger(1, 1e18); const NUM_SAMPLES = 16; @@ -160,6 +163,7 @@ describe('DexSampler tests', () => { expectedMakerToken, expectedTakerToken, [toBaseUnitAmount(1000)], + wethAddress, registry, ), ); @@ -193,6 +197,7 @@ describe('DexSampler tests', () => { expectedMakerToken, expectedTakerToken, [toBaseUnitAmount(1000)], + wethAddress, registry, ), ); @@ -255,6 +260,28 @@ describe('DexSampler tests', () => { expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); }); + it('getUniswapV2SellQuotes()', async () => { + const expectedTakerToken = randomAddress(); + const expectedMakerToken = randomAddress(); + const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); + const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 10); + const sampler = new MockSamplerContract({ + sampleSellsFromUniswapV2: (path, fillAmounts) => { + expect(path).to.deep.eq([expectedMakerToken, expectedTakerToken]); + expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); + return expectedMakerFillAmounts; + }, + }); + const dexOrderSampler = new DexOrderSampler(sampler); + const [fillableAmounts] = await dexOrderSampler.executeAsync( + DexOrderSampler.ops.getUniswapV2SellQuotes( + [expectedMakerToken, expectedTakerToken], + expectedTakerFillAmounts, + ), + ); + expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts); + }); + it('getEth2DaiBuyQuotes()', async () => { const expectedTakerToken = randomAddress(); const expectedMakerToken = randomAddress(); @@ -310,11 +337,17 @@ describe('DexSampler tests', () => { it('getSellQuotes()', async () => { const expectedTakerToken = randomAddress(); const expectedMakerToken = randomAddress(); - const sources = [ERC20BridgeSource.Kyber, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap]; + const sources = [ + ERC20BridgeSource.Kyber, + ERC20BridgeSource.Eth2Dai, + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, + ]; const ratesBySource: RatesBySource = { [ERC20BridgeSource.Kyber]: getRandomFloat(0, 100), [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), + [ERC20BridgeSource.UniswapV2]: getRandomFloat(0, 100), }; const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); const sampler = new MockSamplerContract({ @@ -336,6 +369,11 @@ describe('DexSampler tests', () => { expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); }, + sampleSellsFromUniswapV2: (path, fillAmounts) => { + expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); + }, }); const dexOrderSampler = new DexOrderSampler(sampler); const [quotes] = await dexOrderSampler.executeAsync( @@ -344,6 +382,43 @@ describe('DexSampler tests', () => { expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts, + wethAddress, + ), + ); + expect(quotes).to.be.length(sources.length); + const expectedQuotes = sources.map(s => + expectedTakerFillAmounts.map(a => ({ + source: s, + input: a, + output: a.times(ratesBySource[s]).integerValue(), + })), + ); + expect(quotes).to.deep.eq(expectedQuotes); + }); + + it('getSellQuotes() includes ETH for Uniswap_V2_ETH', async () => { + const expectedTakerToken = randomAddress(); + const expectedMakerToken = randomAddress(); + const sources = [ERC20BridgeSource.UniswapV2Eth]; + const ratesBySource: RatesBySource = { + [ERC20BridgeSource.UniswapV2Eth]: getRandomFloat(0, 100), + }; + const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); + const sampler = new MockSamplerContract({ + sampleSellsFromUniswapV2: (path, fillAmounts) => { + expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); + expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2Eth]).integerValue()); + }, + }); + const dexOrderSampler = new DexOrderSampler(sampler); + const [quotes] = await dexOrderSampler.executeAsync( + DexOrderSampler.ops.getSellQuotes( + sources, + expectedMakerToken, + expectedTakerToken, + expectedTakerFillAmounts, + wethAddress, ), ); expect(quotes).to.be.length(sources.length); @@ -364,6 +439,7 @@ describe('DexSampler tests', () => { const ratesBySource: RatesBySource = { [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), + [ERC20BridgeSource.UniswapV2]: getRandomFloat(0, 100), }; const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); const sampler = new MockSamplerContract({ @@ -379,6 +455,11 @@ describe('DexSampler tests', () => { expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); }, + sampleBuysFromUniswapV2: (path, fillAmounts) => { + expect(path).to.deep.eq([expectedTakerToken, expectedMakerToken]); + expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); + }, }); const dexOrderSampler = new DexOrderSampler(sampler); const [quotes] = await dexOrderSampler.executeAsync( @@ -387,6 +468,56 @@ describe('DexSampler tests', () => { expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts, + wethAddress, + ), + ); + expect(quotes).to.be.length(sources.length); + const expectedQuotes = sources.map(s => + expectedMakerFillAmounts.map(a => ({ + source: s, + input: a, + output: a.times(ratesBySource[s]).integerValue(), + })), + ); + expect(quotes).to.deep.eq(expectedQuotes); + }); + it('getBuyQuotes() includes ETH for Uniswap_V2_ETH', async () => { + const expectedTakerToken = randomAddress(); + const expectedMakerToken = randomAddress(); + const sources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap, ERC20BridgeSource.UniswapV2Eth]; + const ratesBySource: RatesBySource = { + [ERC20BridgeSource.Eth2Dai]: getRandomFloat(0, 100), + [ERC20BridgeSource.Uniswap]: getRandomFloat(0, 100), + [ERC20BridgeSource.UniswapV2Eth]: getRandomFloat(0, 100), + }; + const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3); + const sampler = new MockSamplerContract({ + sampleBuysFromUniswap: (takerToken, makerToken, fillAmounts) => { + expect(takerToken).to.eq(expectedTakerToken); + expect(makerToken).to.eq(expectedMakerToken); + expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()); + }, + sampleBuysFromEth2Dai: (takerToken, makerToken, fillAmounts) => { + expect(takerToken).to.eq(expectedTakerToken); + expect(makerToken).to.eq(expectedMakerToken); + expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()); + }, + sampleBuysFromUniswapV2: (path, fillAmounts) => { + expect(path).to.deep.eq([expectedTakerToken, wethAddress, expectedMakerToken]); + expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts); + return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2Eth]).integerValue()); + }, + }); + const dexOrderSampler = new DexOrderSampler(sampler); + const [quotes] = await dexOrderSampler.executeAsync( + DexOrderSampler.ops.getBuyQuotes( + sources, + expectedMakerToken, + expectedTakerToken, + expectedMakerFillAmounts, + wethAddress, ), ); expect(quotes).to.be.length(sources.length); diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index f3ddb69feb..4549dd6966 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -34,6 +34,7 @@ describe('MarketOperationUtils tests', () => { const ETH2DAI_BRIDGE_ADDRESS = contractAddresses.eth2DaiBridge; const KYBER_BRIDGE_ADDRESS = contractAddresses.kyberBridge; const UNISWAP_BRIDGE_ADDRESS = contractAddresses.uniswapBridge; + const UNISWAP_V2_BRIDGE_ADDRESS = contractAddresses.uniswapV2Bridge; const CURVE_BRIDGE_ADDRESS = contractAddresses.curveBridge; const MAKER_TOKEN = randomAddress(); @@ -89,6 +90,8 @@ describe('MarketOperationUtils tests', () => { return ERC20BridgeSource.Eth2Dai; case UNISWAP_BRIDGE_ADDRESS.toLowerCase(): return ERC20BridgeSource.Uniswap; + case UNISWAP_V2_BRIDGE_ADDRESS.toLowerCase(): + return ERC20BridgeSource.UniswapV2; case CURVE_BRIDGE_ADDRESS.toLowerCase(): const curveSource = Object.keys(DEFAULT_CURVE_OPTS).filter( k => assetData.indexOf(DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1, @@ -151,11 +154,18 @@ describe('MarketOperationUtils tests', () => { makerToken: string, takerToken: string, fillAmounts: BigNumber[], + wethAddress: string, liquidityProviderAddress?: string, ) => DexSample[][]; function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { - return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => { + return ( + sources: ERC20BridgeSource[], + makerToken: string, + takerToken: string, + fillAmounts: BigNumber[], + wethAddress: string, + ) => { return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s])); }; } @@ -173,17 +183,31 @@ describe('MarketOperationUtils tests', () => { makerToken: string, takerToken: string, fillAmounts: BigNumber[], + wethAddress: string, liquidityProviderAddress?: string, ) => { liquidityPoolParams.liquidityProviderAddress = liquidityProviderAddress; liquidityPoolParams.sources = sources; - return tradeOperation(rates)(sources, makerToken, takerToken, fillAmounts, liquidityProviderAddress); + return tradeOperation(rates)( + sources, + makerToken, + takerToken, + fillAmounts, + wethAddress, + liquidityProviderAddress, + ); }; return [liquidityPoolParams, fn]; } function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { - return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => { + return ( + sources: ERC20BridgeSource[], + makerToken: string, + takerToken: string, + fillAmounts: BigNumber[], + wethAddress: string, + ) => { return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s].map(r => new BigNumber(1).div(r)))); }; } @@ -193,6 +217,7 @@ describe('MarketOperationUtils tests', () => { makerToken: string, takerToken: string, fillAmounts: BigNumber[], + wethAddress: string, liquidityProviderAddress?: string, ) => BigNumber; @@ -203,7 +228,13 @@ describe('MarketOperationUtils tests', () => { ) => string; function createGetMedianSellRate(rate: Numberish): GetMedianRateOperation { - return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => { + return ( + sources: ERC20BridgeSource[], + makerToken: string, + takerToken: string, + fillAmounts: BigNumber[], + wethAddress: string, + ) => { return new BigNumber(rate); }; } @@ -255,6 +286,8 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES), [ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES), [ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES), + [ERC20BridgeSource.UniswapV2]: createDecreasingRates(NUM_SAMPLES), + [ERC20BridgeSource.UniswapV2Eth]: createDecreasingRates(NUM_SAMPLES), [ERC20BridgeSource.CurveUsdcDai]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CurveUsdcDaiUsdt]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: _.times(NUM_SAMPLES, () => 0), @@ -310,7 +343,11 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[], + excludedSources: [ + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2Eth, + ...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), + ], allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -323,9 +360,9 @@ describe('MarketOperationUtils tests', () => { const numSamples = _.random(1, NUM_SAMPLES); let actualNumSamples = 0; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts) => { + getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { actualNumSamples = amounts.length; - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -338,9 +375,9 @@ describe('MarketOperationUtils tests', () => { it('polls all DEXes if `excludedSources` is empty', async () => { let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts) => { + getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { sourcesPolled = sources.slice(); - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -379,9 +416,9 @@ describe('MarketOperationUtils tests', () => { const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length)); let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getSellQuotes: (sources, makerToken, takerToken, amounts) => { + getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { sourcesPolled = sources.slice(); - return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getSellQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -445,7 +482,7 @@ describe('MarketOperationUtils tests', () => { it('can mix convex sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; + rates[ERC20BridgeSource.UniswapV2] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; // unused replaceSamplerOps({ @@ -459,7 +496,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Native, ERC20BridgeSource.Native, ]; @@ -474,7 +511,7 @@ describe('MarketOperationUtils tests', () => { const nativeFeeRate = 0.06; const rates: RatesBySource = { [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, 0.93, 0.92, 0.91] - [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], + [ERC20BridgeSource.UniswapV2]: [0.96, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], }; @@ -495,7 +532,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Native, ]; @@ -511,7 +548,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], + [ERC20BridgeSource.UniswapV2]: [1, 0.7, 0.2, 0.2], }; const feeSchedule = { [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) @@ -531,7 +568,7 @@ describe('MarketOperationUtils tests', () => { const expectedSources = [ ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); }); @@ -540,7 +577,7 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = { [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], // Won't use [ERC20BridgeSource.Eth2Dai]: [0.5, 0.85, 0.75, 0.75], // Concave - [ERC20BridgeSource.Uniswap]: [0.96, 0.2, 0.1, 0.1], + [ERC20BridgeSource.UniswapV2]: [0.96, 0.2, 0.1, 0.1], [ERC20BridgeSource.Native]: [0.95, 0.2, 0.2, 0.1], }; replaceSamplerOps({ @@ -555,7 +592,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Native, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); @@ -564,7 +601,7 @@ describe('MarketOperationUtils tests', () => { it('fallback orders use different sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; - rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01]; replaceSamplerOps({ @@ -580,7 +617,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ]; const secondSources = [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber]; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); @@ -590,7 +627,7 @@ describe('MarketOperationUtils tests', () => { it('does not create a fallback if below maxFallbackSlippage', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01]; replaceSamplerOps({ @@ -602,7 +639,7 @@ describe('MarketOperationUtils tests', () => { { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, ); const orderSources = improvedOrders.map(o => o.fills[0].source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; + const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV2]; const secondSources: ERC20BridgeSource[] = []; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); @@ -666,7 +703,7 @@ describe('MarketOperationUtils tests', () => { it('batches contiguous bridge sources', async () => { const rates: RatesBySource = {}; - rates[ERC20BridgeSource.Uniswap] = [1, 0.01, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [1, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.CurveUsdcDai] = [0.48, 0.01, 0.01, 0.01]; @@ -689,7 +726,7 @@ describe('MarketOperationUtils tests', () => { expect(improvedOrders).to.be.length(3); const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source)); expect(orderFillSources).to.deep.eq([ - [ERC20BridgeSource.Uniswap], + [ERC20BridgeSource.UniswapV2], [ERC20BridgeSource.Native], [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.CurveUsdcDai], ]); @@ -707,7 +744,12 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: [...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), ERC20BridgeSource.Kyber], + excludedSources: [ + ...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), + ERC20BridgeSource.Kyber, + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2Eth, + ], allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -720,9 +762,9 @@ describe('MarketOperationUtils tests', () => { const numSamples = _.random(1, 16); let actualNumSamples = 0; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts) => { + getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { actualNumSamples = amounts.length; - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -735,9 +777,9 @@ describe('MarketOperationUtils tests', () => { it('polls all DEXes if `excludedSources` is empty', async () => { let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts) => { + getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { sourcesPolled = sources.slice(); - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -776,9 +818,9 @@ describe('MarketOperationUtils tests', () => { const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length)); let sourcesPolled: ERC20BridgeSource[] = []; replaceSamplerOps({ - getBuyQuotes: (sources, makerToken, takerToken, amounts) => { + getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => { sourcesPolled = sources.slice(); - return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts); + return DEFAULT_OPS.getBuyQuotes(sources, makerToken, takerToken, amounts, wethAddress); }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { @@ -842,7 +884,7 @@ describe('MarketOperationUtils tests', () => { it('can mix convex sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; - rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; + rates[ERC20BridgeSource.UniswapV2] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), @@ -855,7 +897,7 @@ describe('MarketOperationUtils tests', () => { const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Native, ERC20BridgeSource.Native, ]; @@ -870,7 +912,7 @@ describe('MarketOperationUtils tests', () => { const nativeFeeRate = 0.06; const rates: RatesBySource = { [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91] - [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], + [ERC20BridgeSource.UniswapV2]: [0.96, 0.1, 0.1, 0.1], [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], }; @@ -890,7 +932,7 @@ describe('MarketOperationUtils tests', () => { ); const orderSources = improvedOrders.map(o => o.fills[0].source); const expectedSources = [ - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Native, ERC20BridgeSource.Native, @@ -905,11 +947,11 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = { [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] - [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], + [ERC20BridgeSource.UniswapV2]: [1, 0.7, 0.2, 0.2], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], }; const feeSchedule = { - [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) + [ERC20BridgeSource.UniswapV2]: FILL_AMOUNT.div(4) .times(uniswapFeeRate) .dividedToIntegerBy(ETH_TO_TAKER_RATE), }; @@ -926,7 +968,7 @@ describe('MarketOperationUtils tests', () => { const expectedSources = [ ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ]; expect(orderSources.sort()).to.deep.eq(expectedSources.sort()); }); @@ -934,7 +976,7 @@ describe('MarketOperationUtils tests', () => { it('fallback orders use different sources', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; - rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), @@ -949,7 +991,7 @@ describe('MarketOperationUtils tests', () => { ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Native, - ERC20BridgeSource.Uniswap, + ERC20BridgeSource.UniswapV2, ]; const secondSources = [ERC20BridgeSource.Eth2Dai]; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); @@ -959,7 +1001,7 @@ describe('MarketOperationUtils tests', () => { it('does not create a fallback if below maxFallbackSlippage', async () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; - rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), @@ -970,7 +1012,7 @@ describe('MarketOperationUtils tests', () => { { ...DEFAULT_OPTS, numSamples: 4, allowFallback: true, maxFallbackSlippage: 0.25 }, ); const orderSources = improvedOrders.map(o => o.fills[0].source); - const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.Uniswap]; + const firstSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV2]; const secondSources: ERC20BridgeSource[] = []; expect(orderSources.slice(0, firstSources.length).sort()).to.deep.eq(firstSources.sort()); expect(orderSources.slice(firstSources.length).sort()).to.deep.eq(secondSources.sort()); @@ -980,7 +1022,7 @@ describe('MarketOperationUtils tests', () => { const rates: RatesBySource = {}; rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; - rates[ERC20BridgeSource.Uniswap] = [0.48, 0.47, 0.01, 0.01]; + rates[ERC20BridgeSource.UniswapV2] = [0.48, 0.47, 0.01, 0.01]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -997,7 +1039,7 @@ describe('MarketOperationUtils tests', () => { const orderFillSources = improvedOrders.map(o => o.fills.map(f => f.source)); expect(orderFillSources).to.deep.eq([ [ERC20BridgeSource.Native], - [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap], + [ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.UniswapV2], ]); }); }); diff --git a/packages/asset-swapper/test/utils/mock_sampler_contract.ts b/packages/asset-swapper/test/utils/mock_sampler_contract.ts index 8eb7320afb..cfa68f7efd 100644 --- a/packages/asset-swapper/test/utils/mock_sampler_contract.ts +++ b/packages/asset-swapper/test/utils/mock_sampler_contract.ts @@ -21,12 +21,14 @@ export type SampleBuysHandler = ( makerToken: string, makerTokenAmounts: BigNumber[], ) => SampleResults; +export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; export type SampleSellsLPHandler = ( registryAddress: string, takerToken: string, makerToken: string, takerTokenAmounts: BigNumber[], ) => SampleResults; +export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; const DUMMY_PROVIDER = { sendAsync: (...args: any[]): any => { @@ -41,8 +43,10 @@ interface Handlers { sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler; sampleSellsFromEth2Dai: SampleSellsHandler; sampleSellsFromUniswap: SampleSellsHandler; + sampleSellsFromUniswapV2: SampleSellsMultihopHandler; sampleBuysFromEth2Dai: SampleBuysHandler; sampleBuysFromUniswap: SampleBuysHandler; + sampleBuysFromUniswapV2: SampleBuysMultihopHandler; sampleBuysFromLiquidityProviderRegistry: SampleSellsLPHandler; } @@ -127,6 +131,18 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract { ); } + public sampleSellsFromUniswapV2( + path: string[], + takerAssetAmounts: BigNumber[], + ): ContractFunctionObj { + return this._wrapCall( + super.sampleSellsFromUniswapV2, + this._handlers.sampleSellsFromUniswapV2, + path, + takerAssetAmounts, + ); + } + public sampleSellsFromLiquidityProviderRegistry( registryAddress: string, takerToken: string, @@ -171,6 +187,18 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract { ); } + public sampleBuysFromUniswapV2( + path: string[], + makerAssetAmounts: BigNumber[], + ): ContractFunctionObj { + return this._wrapCall( + super.sampleBuysFromUniswapV2, + this._handlers.sampleBuysFromUniswapV2, + path, + makerAssetAmounts, + ); + } + private _callEncodedFunction(callData: string): string { // tslint:disable-next-line: custom-no-magic-numbers const selector = hexUtils.slice(callData, 0, 4); diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 05e5613b99..5b9a0ca596 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -57,6 +57,14 @@ { "note": "Redeploy ERC20BridgeSampler on Mainnet using `Kyber.searchBestRate`", "pr": 2575 + }, + { + "note": "Redeploy ERC20BridgeSampler on Mainnet and Kovan using UniswapV2", + "pr": 2599 + }, + { + "note": "Add UniswapV2Bridge address on Mainnet (new field)", + "pr": 2599 } ] }, diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 015bb0376a..6bc26d85af 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -20,7 +20,8 @@ "devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547", "erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0", "uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad", - "erc20BridgeSampler": "0xc0154b14cc60a6661171fdc817f759429a57e184", + "uniswapV2Bridge": "0xdcd6011f4c6b80e470d9487f5871a0cba7c93f48", + "erc20BridgeSampler": "0xdfec429f7d87bd9911211bbb3b8b83df0f37ebc7", "kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610", "eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1", "chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438", @@ -53,6 +54,7 @@ "stakingProxy": "0xfaabcee42ab6b9c649794ac6c133711071897ee9", "erc20BridgeProxy": "0x599b340b5045436a99b1f0c718d30f5a0c8519dd", "uniswapBridge": "0x0000000000000000000000000000000000000000", + "uniswapV2Bridge": "0x0000000000000000000000000000000000000000", "eth2DaiBridge": "0x0000000000000000000000000000000000000000", "erc20BridgeSampler": "0x0000000000000000000000000000000000000000", "kyberBridge": "0x0000000000000000000000000000000000000000", @@ -86,6 +88,7 @@ "stakingProxy": "0xc6ad5277ea225ac05e271eb14a7ebb480cd9dd9f", "erc20BridgeProxy": "0x31b8653642110f17bdb1f719901d7e7d49b08141", "uniswapBridge": "0x0000000000000000000000000000000000000000", + "uniswapV2Bridge": "0x0000000000000000000000000000000000000000", "eth2DaiBridge": "0x0000000000000000000000000000000000000000", "erc20BridgeSampler": "0x0000000000000000000000000000000000000000", "kyberBridge": "0x0000000000000000000000000000000000000000", @@ -119,6 +122,7 @@ "stakingProxy": "0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34", "erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64", "uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d", + "uniswapV2Bridge": "0x0000000000000000000000000000000000000000", "eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b", "erc20BridgeSampler": "0x26215b52c57317b5ce7380d3e289b51c7d237084", "kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44", @@ -152,6 +156,7 @@ "staking": "0x8a063452f7df2614db1bca3a85ef35da40cf0835", "stakingProxy": "0x59adefa01843c627ba5d6aa350292b4b7ccae67a", "uniswapBridge": "0x0000000000000000000000000000000000000000", + "uniswapV2Bridge": "0x0000000000000000000000000000000000000000", "eth2DaiBridge": "0x0000000000000000000000000000000000000000", "erc20BridgeSampler": "0x2c530e4ecc573f11bd72cf5fdf580d134d25f15f", "kyberBridge": "0x0000000000000000000000000000000000000000", diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index e6a1289bd3..97abe2d50c 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -22,6 +22,7 @@ export interface ContractAddresses { erc20BridgeProxy: string; erc20BridgeSampler: string; uniswapBridge: string; + uniswapV2Bridge: string; eth2DaiBridge: string; kyberBridge: string; chaiBridge: string; diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index 728ec259a5..7b684c83d1 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -9,6 +9,10 @@ { "note": "Add `ERC20BridgeSampler` deployment", "pr": 2541 + }, + { + "note": "Added `UniswapV2Bridge` address on Mainnet", + "pr": 2599 } ] }, diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index 8b25d6ab3e..59e59604bb 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -311,6 +311,7 @@ export async function runMigrationsAsync( staking: stakingLogic.address, stakingProxy: stakingProxy.address, uniswapBridge: constants.NULL_ADDRESS, + uniswapV2Bridge: constants.NULL_ADDRESS, eth2DaiBridge: constants.NULL_ADDRESS, kyberBridge: constants.NULL_ADDRESS, erc20BridgeSampler: erc20BridgeSampler.address,