Merge branch 'development' into feat/asset-swapper/use-quote-server

This commit is contained in:
F. Eugene Aumson
2020-06-16 10:26:56 -04:00
103 changed files with 3668 additions and 542 deletions

View File

@@ -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,
),
);
@@ -207,6 +212,48 @@ describe('DexSampler tests', () => {
]);
});
it('getMultiBridgeSellQuotes()', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const multiBridge = randomAddress();
const sampler = new MockSamplerContract({
sampleSellsFromMultiBridge: (
multiBridgeAddress,
takerToken,
_intermediateToken,
makerToken,
_fillAmounts,
) => {
expect(multiBridgeAddress).to.eq(multiBridge);
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
return [toBaseUnitAmount(1001)];
},
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [result] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getSellQuotes(
[ERC20BridgeSource.MultiBridge],
expectedMakerToken,
expectedTakerToken,
[toBaseUnitAmount(1000)],
randomAddress(),
randomAddress(),
multiBridge,
),
);
expect(result).to.deep.equal([
[
{
source: 'MultiBridge',
output: toBaseUnitAmount(1001),
input: toBaseUnitAmount(1000),
},
],
]);
});
it('getEth2DaiSellQuotes()', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
@@ -255,6 +302,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 +379,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 +411,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 +424,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 +481,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 +497,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 +510,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);

View File

@@ -0,0 +1,268 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { constants as contractConstants, getRandomInteger, Numberish, randomAddress } from '@0x/contracts-test-utils';
import {
decodeFillQuoteTransformerData,
decodePayTakerTransformerData,
decodeWethTransformerData,
ETH_TOKEN_ADDRESS,
FillQuoteTransformerSide,
} from '@0x/contracts-zero-ex';
import { assetDataUtils } from '@0x/order-utils';
import { Order } from '@0x/types';
import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { constants } from '../src/constants';
import {
ExchangeProxySwapQuoteConsumer,
getTransformerAddress,
} from '../src/quote_consumers/exchange_proxy_swap_quote_consumer';
import { MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types';
import { OptimizedMarketOrder } from '../src/utils/market_operation_utils/types';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
const { NULL_ADDRESS } = constants;
const { MAX_UINT256 } = contractConstants;
// tslint:disable: custom-no-magic-numbers
describe('ExchangeProxySwapQuoteConsumer', () => {
const CHAIN_ID = 1;
const TAKER_TOKEN = randomAddress();
const MAKER_TOKEN = randomAddress();
const TRANSFORMER_DEPLOYER = randomAddress();
const contractAddresses = {
...getContractAddressesForChainOrThrow(CHAIN_ID),
exchangeProxy: randomAddress(),
exchangeProxyAllowanceTarget: randomAddress(),
exchangeProxyTransformerDeployer: TRANSFORMER_DEPLOYER,
transformers: {
wethTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 1),
payTakerTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 2),
fillQuoteTransformer: getTransformerAddress(TRANSFORMER_DEPLOYER, 3),
},
};
let consumer: ExchangeProxySwapQuoteConsumer;
before(async () => {
const fakeProvider = {
async sendAsync(): Promise<void> {
/* noop */
},
};
consumer = new ExchangeProxySwapQuoteConsumer(fakeProvider, contractAddresses, { chainId: CHAIN_ID });
});
function getRandomAmount(maxAmount: Numberish = '1e18'): BigNumber {
return getRandomInteger(1, maxAmount);
}
function createAssetData(token?: string): string {
return assetDataUtils.encodeERC20AssetData(token || randomAddress());
}
function getRandomOrder(): OptimizedMarketOrder {
return {
fillableMakerAssetAmount: getRandomAmount(),
fillableTakerFeeAmount: getRandomAmount(),
fillableTakerAssetAmount: getRandomAmount(),
fills: [],
chainId: CHAIN_ID,
exchangeAddress: contractAddresses.exchange,
expirationTimeSeconds: getRandomInteger(1, 2e9),
feeRecipientAddress: randomAddress(),
makerAddress: randomAddress(),
makerAssetAmount: getRandomAmount(),
takerAssetAmount: getRandomAmount(),
makerFee: getRandomAmount(),
takerFee: getRandomAmount(),
salt: getRandomAmount(2e9),
signature: hexUtils.random(66),
senderAddress: NULL_ADDRESS,
takerAddress: NULL_ADDRESS,
makerAssetData: createAssetData(MAKER_TOKEN),
takerAssetData: createAssetData(TAKER_TOKEN),
makerFeeAssetData: createAssetData(),
takerFeeAssetData: createAssetData(),
};
}
function getRandomQuote(side: MarketOperation): MarketBuySwapQuote | MarketSellSwapQuote {
return {
gasPrice: getRandomInteger(1, 1e9),
type: side,
makerAssetData: createAssetData(MAKER_TOKEN),
takerAssetData: createAssetData(TAKER_TOKEN),
orders: [getRandomOrder()],
bestCaseQuoteInfo: {
feeTakerAssetAmount: getRandomAmount(),
makerAssetAmount: getRandomAmount(),
gas: Math.floor(Math.random() * 8e6),
protocolFeeInWeiAmount: getRandomAmount(),
takerAssetAmount: getRandomAmount(),
totalTakerAssetAmount: getRandomAmount(),
},
worstCaseQuoteInfo: {
feeTakerAssetAmount: getRandomAmount(),
makerAssetAmount: getRandomAmount(),
gas: Math.floor(Math.random() * 8e6),
protocolFeeInWeiAmount: getRandomAmount(),
takerAssetAmount: getRandomAmount(),
totalTakerAssetAmount: getRandomAmount(),
},
...(side === MarketOperation.Buy
? { makerAssetFillAmount: getRandomAmount() }
: { takerAssetFillAmount: getRandomAmount() }),
} as any;
}
function getRandomSellQuote(): MarketSellSwapQuote {
return getRandomQuote(MarketOperation.Sell) as MarketSellSwapQuote;
}
function getRandomBuyQuote(): MarketBuySwapQuote {
return getRandomQuote(MarketOperation.Buy) as MarketBuySwapQuote;
}
type PlainOrder = Exclude<Order, ['chainId', 'exchangeAddress']>;
function cleanOrders(orders: OptimizedMarketOrder[]): PlainOrder[] {
return orders.map(
o =>
_.omit(o, [
'chainId',
'exchangeAddress',
'fillableMakerAssetAmount',
'fillableTakerAssetAmount',
'fillableTakerFeeAmount',
'fills',
'signature',
]) as PlainOrder,
);
}
const callDataEncoder = AbiEncoder.createMethod('transformERC20', [
{ type: 'address', name: 'inputToken' },
{ type: 'address', name: 'outputToken' },
{ type: 'uint256', name: 'inputTokenAmount' },
{ type: 'uint256', name: 'minOutputTokenAmount' },
{
type: 'tuple[]',
name: 'transformations',
components: [{ type: 'uint32', name: 'deploymentNonce' }, { type: 'bytes', name: 'data' }],
},
]);
interface CallArgs {
inputToken: string;
outputToken: string;
inputTokenAmount: BigNumber;
minOutputTokenAmount: BigNumber;
transformations: Array<{
deploymentNonce: BigNumber;
data: string;
}>;
}
describe('getCalldataOrThrow()', () => {
it('can produce a sell quote', async () => {
const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
expect(callArgs.inputToken).to.eq(TAKER_TOKEN);
expect(callArgs.outputToken).to.eq(MAKER_TOKEN);
expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAssetAmount);
expect(callArgs.transformations).to.be.length(2);
expect(
callArgs.transformations[0].deploymentNonce.toNumber() ===
consumer.transformerNonces.fillQuoteTransformer,
);
expect(
callArgs.transformations[1].deploymentNonce.toNumber() ===
consumer.transformerNonces.payTakerTransformer,
);
const fillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data);
expect(fillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Sell);
expect(fillQuoteTransformerData.fillAmount).to.bignumber.eq(quote.takerAssetFillAmount);
expect(fillQuoteTransformerData.orders).to.deep.eq(cleanOrders(quote.orders));
expect(fillQuoteTransformerData.signatures).to.deep.eq(quote.orders.map(o => o.signature));
expect(fillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN);
expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, MAKER_TOKEN, ETH_TOKEN_ADDRESS]);
});
it('can produce a buy quote', async () => {
const quote = getRandomBuyQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
expect(callArgs.inputToken).to.eq(TAKER_TOKEN);
expect(callArgs.outputToken).to.eq(MAKER_TOKEN);
expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
expect(callArgs.minOutputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.makerAssetAmount);
expect(callArgs.transformations).to.be.length(2);
expect(
callArgs.transformations[0].deploymentNonce.toNumber() ===
consumer.transformerNonces.fillQuoteTransformer,
);
expect(
callArgs.transformations[1].deploymentNonce.toNumber() ===
consumer.transformerNonces.payTakerTransformer,
);
const fillQuoteTransformerData = decodeFillQuoteTransformerData(callArgs.transformations[0].data);
expect(fillQuoteTransformerData.side).to.eq(FillQuoteTransformerSide.Buy);
expect(fillQuoteTransformerData.fillAmount).to.bignumber.eq(quote.makerAssetFillAmount);
expect(fillQuoteTransformerData.orders).to.deep.eq(cleanOrders(quote.orders));
expect(fillQuoteTransformerData.signatures).to.deep.eq(quote.orders.map(o => o.signature));
expect(fillQuoteTransformerData.sellToken).to.eq(TAKER_TOKEN);
expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, MAKER_TOKEN, ETH_TOKEN_ADDRESS]);
});
it('ERC20 -> ERC20 does not have a WETH transformer', async () => {
const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
const nonces = callArgs.transformations.map(t => t.deploymentNonce);
expect(nonces).to.not.include(consumer.transformerNonces.wethTransformer);
});
it('ETH -> ERC20 has a WETH transformer before the fill', async () => {
const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { isFromETH: true },
});
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
expect(callArgs.transformations[0].deploymentNonce.toNumber()).to.eq(
consumer.transformerNonces.wethTransformer,
);
const wethTransformerData = decodeWethTransformerData(callArgs.transformations[0].data);
expect(wethTransformerData.amount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
expect(wethTransformerData.token).to.eq(ETH_TOKEN_ADDRESS);
});
it('ERC20 -> ETH has a WETH transformer after the fill', async () => {
const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { isToETH: true },
});
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs;
expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq(
consumer.transformerNonces.wethTransformer,
);
const wethTransformerData = decodeWethTransformerData(callArgs.transformations[1].data);
expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256);
expect(wethTransformerData.token).to.eq(contractAddresses.etherToken);
});
});
});

View File

@@ -30,10 +30,11 @@ import { DexSample, ERC20BridgeSource, NativeFillData } from '../src/utils/marke
// tslint:disable: custom-no-magic-numbers
describe('MarketOperationUtils tests', () => {
const CHAIN_ID = 1;
const contractAddresses = getContractAddressesForChainOrThrow(CHAIN_ID);
const contractAddresses = { ...getContractAddressesForChainOrThrow(CHAIN_ID), multiBridge: NULL_ADDRESS };
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);
};
}
@@ -228,7 +259,9 @@ describe('MarketOperationUtils tests', () => {
const fn = (registryAddress: string, takerToken: string, makerToken: string): string => {
callArgs.makerToken = makerToken;
callArgs.takerToken = takerToken;
callArgs.registryAddress = registryAddress;
if (registryAddress !== constants.NULL_ADDRESS) {
callArgs.registryAddress = registryAddress;
}
return liquidityProviderAddress;
};
return [callArgs, fn];
@@ -255,12 +288,15 @@ 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),
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.CurveUsdcDaiUsdtSusd]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0),
};
const DEFAULT_OPS = {
@@ -310,7 +346,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 +363,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 +378,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 +419,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 +485,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 +499,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 +514,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 +535,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 +551,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 +571,7 @@ describe('MarketOperationUtils tests', () => {
const expectedSources = [
ERC20BridgeSource.Native,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
];
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
});
@@ -540,7 +580,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 +595,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 +604,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 +620,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 +630,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 +642,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 +706,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 +729,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 +747,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 +765,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 +780,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 +821,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 +887,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 +900,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 +915,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 +935,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 +950,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 +971,7 @@ describe('MarketOperationUtils tests', () => {
const expectedSources = [
ERC20BridgeSource.Native,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
];
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
});
@@ -934,7 +979,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 +994,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 +1004,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 +1015,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 +1025,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 +1042,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],
]);
});
});

View File

@@ -21,12 +21,21 @@ 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;
export type SampleSellsMBHandler = (
multiBridgeAddress: string,
takerToken: string,
intermediateToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
) => SampleResults;
const DUMMY_PROVIDER = {
sendAsync: (...args: any[]): any => {
@@ -39,10 +48,13 @@ interface Handlers {
getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
sampleSellsFromKyberNetwork: SampleSellsHandler;
sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler;
sampleSellsFromMultiBridge: SampleSellsMBHandler;
sampleSellsFromEth2Dai: SampleSellsHandler;
sampleSellsFromUniswap: SampleSellsHandler;
sampleSellsFromUniswapV2: SampleSellsMultihopHandler;
sampleBuysFromEth2Dai: SampleBuysHandler;
sampleBuysFromUniswap: SampleBuysHandler;
sampleBuysFromUniswapV2: SampleBuysMultihopHandler;
sampleBuysFromLiquidityProviderRegistry: SampleSellsLPHandler;
}
@@ -127,6 +139,18 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract {
);
}
public sampleSellsFromUniswapV2(
path: string[],
takerAssetAmounts: BigNumber[],
): ContractFunctionObj<GetOrderFillableAssetAmountResult> {
return this._wrapCall(
super.sampleSellsFromUniswapV2,
this._handlers.sampleSellsFromUniswapV2,
path,
takerAssetAmounts,
);
}
public sampleSellsFromLiquidityProviderRegistry(
registryAddress: string,
takerToken: string,
@@ -143,6 +167,24 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract {
);
}
public sampleSellsFromMultiBridge(
multiBridgeAddress: string,
takerToken: string,
intermediateToken: string,
makerToken: string,
takerAssetAmounts: BigNumber[],
): ContractFunctionObj<GetOrderFillableAssetAmountResult> {
return this._wrapCall(
super.sampleSellsFromMultiBridge,
this._handlers.sampleSellsFromMultiBridge,
multiBridgeAddress,
takerToken,
intermediateToken,
makerToken,
takerAssetAmounts,
);
}
public sampleBuysFromEth2Dai(
takerToken: string,
makerToken: string,
@@ -171,6 +213,18 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract {
);
}
public sampleBuysFromUniswapV2(
path: string[],
makerAssetAmounts: BigNumber[],
): ContractFunctionObj<GetOrderFillableAssetAmountResult> {
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);