@0x/asset-swapper: Refactor market op utils.
`@0x/asset-swapper`: add fallback orders. `@0x/asset-swapper`: Remove `noConflicts` and `dustThreshold` options. `@0x/asset-swapper`: Add `allowFallback` option.
This commit is contained in:
@@ -14,14 +14,11 @@ import { AssetProxyId, ERC20BridgeAssetData, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, fromTokenUnitAmount, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants as assetSwapperConstants } from '../src/constants';
|
||||
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
|
||||
import { constants as marketOperationUtilConstants } from '../src/utils/market_operation_utils/constants';
|
||||
import { BUY_SOURCES, DEFAULT_CURVE_OPTS, SELL_SOURCES } from '../src/utils/market_operation_utils/constants';
|
||||
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
|
||||
import { DexSample, ERC20BridgeSource } from '../src/utils/market_operation_utils/types';
|
||||
|
||||
const { BUY_SOURCES, SELL_SOURCES } = marketOperationUtilConstants;
|
||||
|
||||
// tslint:disable: custom-no-magic-numbers
|
||||
describe('MarketOperationUtils tests', () => {
|
||||
const CHAIN_ID = 1;
|
||||
@@ -81,8 +78,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
case UNISWAP_BRIDGE_ADDRESS.toLowerCase():
|
||||
return ERC20BridgeSource.Uniswap;
|
||||
case CURVE_BRIDGE_ADDRESS.toLowerCase():
|
||||
const curveSource = Object.keys(assetSwapperConstants.DEFAULT_CURVE_OPTS).filter(
|
||||
k => assetData.indexOf(assetSwapperConstants.DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1,
|
||||
const curveSource = Object.keys(DEFAULT_CURVE_OPTS).filter(
|
||||
k => assetData.indexOf(DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1,
|
||||
);
|
||||
return curveSource[0] as ERC20BridgeSource;
|
||||
default:
|
||||
@@ -120,20 +117,21 @@ describe('MarketOperationUtils tests', () => {
|
||||
chainId: CHAIN_ID,
|
||||
};
|
||||
|
||||
type GetQuotesOperation = (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => BigNumber[];
|
||||
|
||||
function createGetSellQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
|
||||
return (...args) => {
|
||||
const fillAmounts = args.pop() as BigNumber[];
|
||||
return fillAmounts.map((a, i) => a.times(rates[i]).integerValue());
|
||||
};
|
||||
}
|
||||
|
||||
function createGetBuyQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
|
||||
return (...args) => {
|
||||
const fillAmounts = args.pop() as BigNumber[];
|
||||
return fillAmounts.map((a, i) => a.div(rates[i]).integerValue());
|
||||
};
|
||||
function createSamplesFromRates(source: ERC20BridgeSource, inputs: Numberish[], rates: Numberish[]): DexSample[] {
|
||||
const samples: DexSample[] = [];
|
||||
inputs.forEach((input, i) => {
|
||||
const rate = rates[i];
|
||||
samples.push({
|
||||
source,
|
||||
input: new BigNumber(input),
|
||||
output: new BigNumber(input)
|
||||
.minus(i === 0 ? 0 : samples[i - 1].input)
|
||||
.times(rate)
|
||||
.plus(i === 0 ? 0 : samples[i - 1].output)
|
||||
.integerValue(),
|
||||
});
|
||||
});
|
||||
return samples;
|
||||
}
|
||||
|
||||
type GetMultipleQuotesOperation = (
|
||||
@@ -146,13 +144,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
|
||||
function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
|
||||
return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return sources.map(s =>
|
||||
fillAmounts.map((a, i) => ({
|
||||
source: s,
|
||||
input: a,
|
||||
output: a.times(rates[s][i]).integerValue(),
|
||||
})),
|
||||
);
|
||||
return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s]));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -180,13 +172,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
|
||||
function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
|
||||
return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return sources.map(s =>
|
||||
fillAmounts.map((a, i) => ({
|
||||
source: s,
|
||||
input: a,
|
||||
output: a.div(rates[s][i]).integerValue(),
|
||||
})),
|
||||
);
|
||||
return sources.map(s => createSamplesFromRates(s, fillAmounts, rates[s].map(r => new BigNumber(1).div(r))));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -264,22 +250,6 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
|
||||
};
|
||||
|
||||
function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource {
|
||||
const minSourceRates = Object.keys(rates).map(s => _.last(rates[s]) as BigNumber);
|
||||
const bestSourceRate = BigNumber.max(...minSourceRates);
|
||||
let source = Object.keys(rates)[_.findIndex(minSourceRates, t => bestSourceRate.eq(t))] as ERC20BridgeSource;
|
||||
// Native order rates play by different rules.
|
||||
if (source !== ERC20BridgeSource.Native) {
|
||||
const nativeTotalRate = BigNumber.sum(...rates[ERC20BridgeSource.Native]).div(
|
||||
rates[ERC20BridgeSource.Native].length,
|
||||
);
|
||||
if (nativeTotalRate.gt(bestSourceRate)) {
|
||||
source = ERC20BridgeSource.Native;
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
const DEFAULT_OPS = {
|
||||
getOrderFillableTakerAmounts(orders: SignedOrder[]): BigNumber[] {
|
||||
return orders.map(o => o.takerAssetAmount);
|
||||
@@ -287,12 +257,6 @@ describe('MarketOperationUtils tests', () => {
|
||||
getOrderFillableMakerAmounts(orders: SignedOrder[]): BigNumber[] {
|
||||
return orders.map(o => o.makerAssetAmount);
|
||||
},
|
||||
getKyberSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Kyber]),
|
||||
getUniswapSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Uniswap]),
|
||||
getEth2DaiSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
|
||||
getUniswapBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Uniswap]),
|
||||
getEth2DaiBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
|
||||
getCurveSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.CurveUsdcDai]),
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES),
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES),
|
||||
getMedianSellRate: createGetMedianSellRate(1),
|
||||
@@ -330,10 +294,10 @@ describe('MarketOperationUtils tests', () => {
|
||||
);
|
||||
const DEFAULT_OPTS = {
|
||||
numSamples: NUM_SAMPLES,
|
||||
runLimit: 0,
|
||||
sampleDistributionBase: 1,
|
||||
bridgeSlippage: 0,
|
||||
excludedSources: Object.keys(assetSwapperConstants.DEFAULT_CURVE_OPTS) as ERC20BridgeSource[],
|
||||
excludedSources: Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[],
|
||||
allowFallback: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -341,7 +305,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('queries `numSamples` samples', async () => {
|
||||
const numSamples = _.random(1, 16);
|
||||
const numSamples = _.random(1, NUM_SAMPLES);
|
||||
let actualNumSamples = 0;
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: (sources, makerToken, takerToken, amounts) => {
|
||||
@@ -412,18 +376,6 @@ describe('MarketOperationUtils tests', () => {
|
||||
expect(sourcesPolled.sort()).to.deep.eq(_.without(SELL_SOURCES, ...excludedSources).sort());
|
||||
});
|
||||
|
||||
it('returns the most cost-effective single source if `runLimit == 0`', async () => {
|
||||
const bestSource = findSourceWithMaxOutput(DEFAULT_RATES);
|
||||
expect(bestSource).to.exist('');
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
runLimit: 0,
|
||||
});
|
||||
const uniqueAssetDatas = _.uniq(improvedOrders.map(o => o.makerAssetData));
|
||||
expect(uniqueAssetDatas).to.be.length(1);
|
||||
expect(getSourceFromAssetData(uniqueAssetDatas[0])).to.be.eq(bestSource);
|
||||
});
|
||||
|
||||
it('generates bridge orders with correct asset data', async () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
// Pass in empty orders to prevent native orders from being used.
|
||||
@@ -469,10 +421,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
);
|
||||
expect(improvedOrders).to.not.be.length(0);
|
||||
for (const order of improvedOrders) {
|
||||
const source = getSourceFromAssetData(order.makerAssetData);
|
||||
const expectedMakerAmount = FILL_AMOUNT.times(_.last(DEFAULT_RATES[source]) as BigNumber);
|
||||
const expectedMakerAmount = order.fill.totalMakerAssetAmount;
|
||||
const slippage = 1 - order.makerAssetAmount.div(expectedMakerAmount.plus(1)).toNumber();
|
||||
assertRoughlyEquals(slippage, bridgeSlippage, 8);
|
||||
assertRoughlyEquals(slippage, bridgeSlippage, 1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -488,19 +439,19 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, noConflicts: false },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('excludes Kyber when `noConflicts` enabled and Uniswap or Eth2Dai are used first', async () => {
|
||||
it('excludes Kyber when Uniswap or Eth2Dai are used first', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
rates[ERC20BridgeSource.Native] = [0.3, 0.2, 0.1, 0.05];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
@@ -512,7 +463,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, noConflicts: true },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
@@ -524,7 +475,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('excludes Uniswap and Eth2Dai when `noConflicts` enabled and Kyber is used first', async () => {
|
||||
it('excludes Uniswap and Eth2Dai when Kyber is used first', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
rates[ERC20BridgeSource.Native] = [0.1, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.15, 0.05, 0.05, 0.05];
|
||||
@@ -536,15 +487,10 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, noConflicts: true },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
const expectedSources = [ERC20BridgeSource.Kyber, ERC20BridgeSource.Native];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
@@ -572,7 +518,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, noConflicts: false, fees },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, fees },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
@@ -587,17 +533,17 @@ describe('MarketOperationUtils tests', () => {
|
||||
it('factors in fees for dexes', async () => {
|
||||
// Kyber will have the best rates but will have fees,
|
||||
// dropping its effective rates.
|
||||
const kyberFeeRate = 0.2;
|
||||
const uniswapFeeRate = 0.2;
|
||||
const rates: RatesBySource = {
|
||||
[ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Uniswap]: [0.1, 0.1, 0.1, 0.1],
|
||||
[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.Kyber]: [1, 0.7, 0.2, 0.2],
|
||||
[ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
|
||||
};
|
||||
const fees = {
|
||||
[ERC20BridgeSource.Kyber]: FILL_AMOUNT.div(4)
|
||||
.times(kyberFeeRate)
|
||||
[ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4)
|
||||
.times(uniswapFeeRate)
|
||||
.dividedToIntegerBy(ETH_TO_MAKER_RATE),
|
||||
};
|
||||
replaceSamplerOps({
|
||||
@@ -607,10 +553,65 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, noConflicts: false, fees },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, fees },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber];
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('can mix one concave source', async () => {
|
||||
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.Native]: [0.95, 0.2, 0.2, 0.1],
|
||||
};
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE),
|
||||
});
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('fallback orders use different sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.9, 0.8, 0.5, 0.5];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
});
|
||||
const improvedOrders = await marketOperationUtils.getMarketSellOrdersAsync(
|
||||
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
// Fallback
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
@@ -679,9 +680,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
);
|
||||
const DEFAULT_OPTS = {
|
||||
numSamples: NUM_SAMPLES,
|
||||
runLimit: 0,
|
||||
sampleDistributionBase: 1,
|
||||
excludedSources: Object.keys(assetSwapperConstants.DEFAULT_CURVE_OPTS) as ERC20BridgeSource[],
|
||||
excludedSources: Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[],
|
||||
allowFallback: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -760,26 +761,6 @@ describe('MarketOperationUtils tests', () => {
|
||||
expect(sourcesPolled).to.deep.eq(_.without(BUY_SOURCES, ...excludedSources));
|
||||
});
|
||||
|
||||
it('returns the most cost-effective single source if `runLimit == 0`', async () => {
|
||||
const bestSource = findSourceWithMaxOutput(
|
||||
_.omit(
|
||||
DEFAULT_RATES,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.CurveUsdcDai,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdt,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
|
||||
),
|
||||
);
|
||||
expect(bestSource).to.exist('');
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
runLimit: 0,
|
||||
});
|
||||
const uniqueAssetDatas = _.uniq(improvedOrders.map(o => o.makerAssetData));
|
||||
expect(uniqueAssetDatas).to.be.length(1);
|
||||
expect(getSourceFromAssetData(uniqueAssetDatas[0])).to.be.eq(bestSource);
|
||||
});
|
||||
|
||||
it('generates bridge orders with correct asset data', async () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
// Pass in empty orders to prevent native orders from being used.
|
||||
@@ -825,10 +806,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
);
|
||||
expect(improvedOrders).to.not.be.length(0);
|
||||
for (const order of improvedOrders) {
|
||||
const source = getSourceFromAssetData(order.makerAssetData);
|
||||
const expectedTakerAmount = FILL_AMOUNT.div(_.last(DEFAULT_RATES[source]) as BigNumber);
|
||||
const expectedTakerAmount = order.fill.totalTakerAssetAmount;
|
||||
const slippage = order.takerAssetAmount.div(expectedTakerAmount.plus(1)).toNumber() - 1;
|
||||
assertRoughlyEquals(slippage, bridgeSlippage, 8);
|
||||
assertRoughlyEquals(slippage, bridgeSlippage, 1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -843,7 +823,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512 },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4 },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
@@ -879,7 +859,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, fees },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, fees },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
@@ -913,7 +893,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, runLimit: 512, fees },
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, fees },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
@@ -923,6 +903,32 @@ describe('MarketOperationUtils tests', () => {
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('fallback orders use different sources', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.9, 0.8, 0.5, 0.5];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.01, 0.01];
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||
});
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
|
||||
FILL_AMOUNT,
|
||||
{ ...DEFAULT_OPTS, numSamples: 4, allowFallback: true },
|
||||
);
|
||||
const orderSources = improvedOrders.map(o => o.fill.source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
// Fallback
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user