chore: FillAdjustor and clean up JS router and unused functions [TKR-403] (#480)

* Remove old JS router and add a FillAdjustor

Clean up JS router and unused functions

Remove more unused functions, add adjustment of fills

Comment on why we use fill over sample

update CODEOWNERS

lint

Clean up Fill removing unused properties

Remove CollapsedFills, omit flags bigint

Create GasSchedule vs FeeSchedule, return Fill and gas on OptimizedOrder

Use Fill Adjustment in Phase2 of routing

Fix Limit orders being treated as VIP

* Fix case where dex liquidity is empty

* Use best gas adjusted pricing for fee sources

* CHANGELOG
This commit is contained in:
Jacob Evans
2022-06-29 18:10:56 +10:00
committed by GitHub
parent 2aadbda527
commit ee985240fb
26 changed files with 709 additions and 1171 deletions

View File

@@ -5,8 +5,8 @@ import * as _ from 'lodash';
import { MarketOperation } from '../src/types';
import {
CollapsedFill,
ERC20BridgeSource,
Fill,
NativeLimitOrderFillData,
OptimizedMarketOrder,
OptimizedMarketOrderBase,
@@ -45,18 +45,16 @@ describe('quote_simulation tests', async () => {
inputFeeRate: number;
outputFeeRate: number;
count: number;
fillsCount: number;
side: MarketOperation;
type?: FillQuoteTransformerOrderType;
}> = {},
): QuoteFillOrderCall[] {
const { fillableInput, fillableOutput, inputFeeRate, outputFeeRate, count, fillsCount, side, type } = {
const { fillableInput, fillableOutput, inputFeeRate, outputFeeRate, count, side, type } = {
fillableInput: getRandomOrderSize(),
fillableOutput: getRandomOrderSize(),
inputFeeRate: 0,
outputFeeRate: 0,
count: 3,
fillsCount: 3,
side: MarketOperation.Sell,
...opts,
};
@@ -83,7 +81,6 @@ describe('quote_simulation tests', async () => {
return {
order: createQuoteFillOrderOrder(totalInputs[i], totalOutputs[i], {
side,
fillsCount,
filledInput: filledInputs[i],
takerInputFee: inputFees[i].abs(),
takerOutputFee: outputFees[i].abs(),
@@ -102,19 +99,17 @@ describe('quote_simulation tests', async () => {
output: BigNumber,
opts: Partial<{
filledInput: BigNumber;
fillsCount: number;
side: MarketOperation;
takerInputFee: BigNumber;
takerOutputFee: BigNumber;
type: FillQuoteTransformerOrderType;
}> = {},
): OptimizedMarketOrderBase<NativeLimitOrderFillData> {
const { filledInput, fillsCount, side, takerInputFee, takerOutputFee, type } = _.merge(
const { filledInput, side, takerInputFee, takerOutputFee, type } = _.merge(
{},
{
side: MarketOperation.Sell,
filledInput: ZERO,
fillsCount: 3,
takerInputFee: ZERO,
takerOutputFee: ZERO,
type: FillQuoteTransformerOrderType.Limit,
@@ -160,46 +155,23 @@ describe('quote_simulation tests', async () => {
maxTakerTokenFillAmount: fillableTakerAmount,
},
type,
fills: createOrderCollapsedFills(fillableInput, fillableOutput, fillsCount),
fill: createOrderFill(fillableInput, fillableOutput),
};
return order;
}
const nativeSourcePathId = hexUtils.random();
function createOrderCollapsedFills(input: BigNumber, output: BigNumber, count: number): CollapsedFill[] {
const inputs = subdivideAmount(input, count);
const outputs = subdivideAmount(output, count);
return _.times(count, i => {
const subFillInputs = subdivideAmount(inputs[i], count);
const subFillOutputs = subdivideAmount(outputs[i], count);
return {
type: FillQuoteTransformerOrderType.Bridge,
sourcePathId: nativeSourcePathId,
source: ERC20BridgeSource.Uniswap,
fillData: {},
input: inputs[i],
output: outputs[i],
subFills: _.times(count, j => ({
input: subFillInputs[j],
output: subFillOutputs[j],
})),
};
});
}
function countCollapsedFills(fillOrders: QuoteFillOrderCall[] | OptimizedMarketOrder[]): number {
let count = 0;
if ((fillOrders as any)[0].fills) {
const orders = (fillOrders as any) as OptimizedMarketOrder[];
for (const o of orders) {
count += o.fills.length;
}
} else {
const orders = (fillOrders as any) as QuoteFillOrderCall[];
for (const fo of orders) {
count += fo.order.fills.length;
}
}
return count;
function createOrderFill(input: BigNumber, output: BigNumber): Fill {
return {
type: FillQuoteTransformerOrderType.Bridge,
sourcePathId: nativeSourcePathId,
source: ERC20BridgeSource.Uniswap,
fillData: {},
input,
output,
flags: BigInt(0),
adjustedOutput: output,
gas: 1,
};
}
function randomSide(): MarketOperation {
@@ -237,14 +209,12 @@ describe('quote_simulation tests', async () => {
describe('single order', () => {
it('can exactly fill one order', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const fillOrders = createQuoteFillOrders({
fillableInput,
fillableOutput,
side,
fillsCount,
count: 1,
});
const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
@@ -253,19 +223,16 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(fillableInput);
assertRoughlyEquals(totalFilledOutput, fillableOutput);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('can partially fill one simple order', () => {
const side = randomSide();
const fillsCount = 1;
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const fillOrders = createQuoteFillOrders({
fillableInput,
fillableOutput,
side,
fillsCount,
count: 1,
});
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
@@ -279,19 +246,16 @@ describe('quote_simulation tests', async () => {
.integerValue();
assertRoughlyEquals(totalFilledOutput, expectedOutputFilledAmount);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(1);
});
it('can partially fill one batched order', () => {
const side = randomSide();
const fillsCount = 3;
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const fillOrders = createQuoteFillOrders({
fillableInput,
fillableOutput,
side,
fillsCount,
count: 1,
});
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
@@ -301,20 +265,16 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
expect(totalFilledOutput).to.bignumber.lt(fillableOutput);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.gte(1);
expect(result.gas).to.lte(fillsCount);
});
it('does not over fill one order', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const fillOrders = createQuoteFillOrders({
fillableInput,
fillableOutput,
side,
fillsCount,
count: 1,
});
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
@@ -324,12 +284,10 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(fillableInput);
assertRoughlyEquals(totalFilledOutput, fillableOutput);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('can exactly fill one order with input fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const inputFeeRate = getRandomFeeRate();
@@ -338,7 +296,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
inputFeeRate,
side,
fillsCount,
count: 1,
});
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
@@ -350,12 +307,10 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('can partially fill one order with input fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const inputFeeRate = getRandomFeeRate();
@@ -364,7 +319,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
inputFeeRate,
side,
fillsCount,
count: 1,
});
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
@@ -377,12 +331,10 @@ describe('quote_simulation tests', async () => {
expect(totalFilledOutput).to.bignumber.lt(fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.lte(fillsCount);
});
it('does not over fill one order with input fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const inputFeeRate = getRandomFeeRate();
@@ -391,7 +343,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
inputFeeRate,
side,
fillsCount,
count: 1,
});
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
@@ -404,12 +355,10 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('can exactly fill one order with output fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const outputFeeRate = getRandomFeeRate();
@@ -418,7 +367,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
outputFeeRate,
side,
fillsCount,
count: 1,
});
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
@@ -430,12 +378,10 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('can partial fill one order with output fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const outputFeeRate = getRandomFeeRate();
@@ -444,7 +390,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
outputFeeRate,
side,
fillsCount,
count: 1,
});
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
@@ -457,12 +402,10 @@ describe('quote_simulation tests', async () => {
expect(totalFilledOutput).to.bignumber.lt(totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.lte(fillsCount);
});
it('does not over fill one order with output fees', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const outputFeeRate = getRandomFeeRate();
@@ -471,7 +414,6 @@ describe('quote_simulation tests', async () => {
fillableOutput,
outputFeeRate,
side,
fillsCount,
count: 1,
});
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
@@ -484,19 +426,16 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.eq(1);
expect(result.gas).to.eq(fillsCount);
});
it('does not charge a protocol fee for rfq orders', () => {
const side = randomSide();
const fillsCount = _.random(1, 3);
const fillableInput = getRandomOrderSize();
const fillableOutput = getRandomOrderSize();
const fillOrders = createQuoteFillOrders({
fillableInput,
fillableOutput,
side,
fillsCount,
count: 1,
type: FillQuoteTransformerOrderType.Rfq,
});
@@ -506,7 +445,6 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(fillableInput);
assertRoughlyEquals(totalFilledOutput, fillableOutput);
expect(result.protocolFee).to.bignumber.eq(0);
expect(result.gas).to.eq(fillsCount);
});
});
@@ -522,7 +460,6 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(fillableInput);
expect(totalFilledOutput).to.bignumber.eq(fillableOutput);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
it('can partial fill orders', () => {
@@ -551,7 +488,6 @@ describe('quote_simulation tests', async () => {
expect(totalFilledInput).to.bignumber.eq(fillableInput);
expect(totalFilledOutput).to.bignumber.eq(fillableOutput);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
it('can exactly fill orders with input fees', () => {
@@ -574,7 +510,6 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
it('can partial fill orders with input fees', () => {
@@ -598,7 +533,6 @@ describe('quote_simulation tests', async () => {
expect(totalFilledOutput).to.bignumber.lt(fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.lte(fillOrders.length);
expect(result.gas).to.lte(countCollapsedFills(fillOrders));
});
it('does not over fill orders with input fees', () => {
@@ -622,7 +556,6 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, fillableOutput);
assertEqualRates(result.inputFee.div(result.input), signedInputFeeRate);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
it('can exactly fill orders with output fees', () => {
@@ -645,7 +578,6 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
it('can partial fill orders with output fees', () => {
@@ -669,7 +601,6 @@ describe('quote_simulation tests', async () => {
expect(totalFilledOutput).to.bignumber.lt(totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.lte(fillOrders.length);
expect(result.gas).to.lte(countCollapsedFills(fillOrders));
});
it('does not over fill orders with output fees', () => {
@@ -693,7 +624,6 @@ describe('quote_simulation tests', async () => {
assertRoughlyEquals(totalFilledOutput, totalFillableOutput);
assertEqualRates(result.outputFee.div(result.output), signedOutputFeeRate);
expect(result.protocolFee).to.bignumber.eq(fillOrders.length);
expect(result.gas).to.eq(countCollapsedFills(fillOrders));
});
});
});
@@ -771,7 +701,6 @@ describe('quote_simulation tests', async () => {
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.eq(countCollapsedFills(orders));
expect(result.protocolFeeAmount).to.bignumber.eq(orders.length);
expect(result.takerFeeTakerAssetAmount).to.bignumber.eq(0);
expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0);
@@ -895,7 +824,6 @@ describe('quote_simulation tests', async () => {
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.eq(countCollapsedFills(orders));
expect(result.protocolFeeAmount).to.bignumber.eq(orders.length);
assertRoughlyEquals(result.makerAssetAmount, fillableInput);