Merge pull request #2526 from 0xProject/fix/asset-swapper/optimizer-fee-bugs
AssetSwapper: Fix quote optimizer fee bug
This commit is contained in:
commit
99b0a95f8a
@ -29,6 +29,10 @@
|
||||
{
|
||||
"note": "Fix fee schedule not being scaled by gas price.",
|
||||
"pr": 2522
|
||||
},
|
||||
{
|
||||
"note": "Fix quote optimizer bug not properly accounting for fees.",
|
||||
"pr": 2526
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -29,8 +29,8 @@ const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
||||
]), // Default asset-swapper for CFL oriented fee types
|
||||
};
|
||||
|
||||
// 15 seconds polling interval
|
||||
const PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS = 15000;
|
||||
// 6 seconds polling interval
|
||||
const PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS = 6000;
|
||||
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000);
|
||||
|
||||
// default 50% buffer for selecting native orders to be aggregated with other sources
|
||||
|
@ -197,7 +197,7 @@ export function getPathAdjustedSize(path: Fill[], targetInput: BigNumber = POSIT
|
||||
return [input.integerValue(), output.integerValue()];
|
||||
}
|
||||
|
||||
export function isValidPath(path: Fill[]): boolean {
|
||||
export function isValidPath(path: Fill[], skipDuplicateCheck: boolean = false): boolean {
|
||||
let flags = 0;
|
||||
for (let i = 0; i < path.length; ++i) {
|
||||
// Fill must immediately follow its parent.
|
||||
@ -206,12 +206,14 @@ export function isValidPath(path: Fill[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!skipDuplicateCheck) {
|
||||
// Fill must not be duplicated.
|
||||
for (let j = 0; j < i; ++j) {
|
||||
if (path[i] === path[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
flags |= path[i].flags;
|
||||
}
|
||||
const conflictFlags = FillFlags.Kyber | FillFlags.ConflictsWithKyber;
|
||||
|
@ -34,7 +34,6 @@ function mixPaths(
|
||||
targetInput: BigNumber,
|
||||
maxSteps: number = 2 ** 15,
|
||||
): Fill[] {
|
||||
const allFills = [...pathA, ...pathB].sort((a, b) => b.rate.comparedTo(a.rate));
|
||||
let bestPath: Fill[] = [];
|
||||
let bestPathInput = ZERO_AMOUNT;
|
||||
let bestPathRate = ZERO_AMOUNT;
|
||||
@ -42,12 +41,12 @@ function mixPaths(
|
||||
const _isBetterPath = (input: BigNumber, rate: BigNumber) => {
|
||||
if (bestPathInput.lt(targetInput)) {
|
||||
return input.gt(bestPathInput);
|
||||
} else if (input.gte(bestPathInput)) {
|
||||
} else if (input.gte(targetInput)) {
|
||||
return rate.gt(bestPathRate);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const _walk = (path: Fill[], input: BigNumber, output: BigNumber) => {
|
||||
const _walk = (path: Fill[], input: BigNumber, output: BigNumber, allFills: Fill[]) => {
|
||||
steps += 1;
|
||||
const rate = getRate(side, input, output);
|
||||
if (_isBetterPath(input, rate)) {
|
||||
@ -55,20 +54,35 @@ function mixPaths(
|
||||
bestPathInput = input;
|
||||
bestPathRate = rate;
|
||||
}
|
||||
if (input.lt(targetInput)) {
|
||||
for (const fill of allFills) {
|
||||
if (steps >= maxSteps) {
|
||||
const remainingInput = targetInput.minus(input);
|
||||
if (remainingInput.gt(0)) {
|
||||
for (let i = 0; i < allFills.length; ++i) {
|
||||
const fill = allFills[i];
|
||||
if (steps + 1 >= maxSteps) {
|
||||
break;
|
||||
}
|
||||
const childPath = [...path, fill];
|
||||
if (!isValidPath(childPath)) {
|
||||
if (!isValidPath(childPath, true)) {
|
||||
continue;
|
||||
}
|
||||
_walk(childPath, input.plus(fill.input), output.plus(fill.adjustedOutput));
|
||||
// Remove this fill from the next list of candidate fills.
|
||||
const nextAllFills = allFills.slice();
|
||||
nextAllFills.splice(i, 1);
|
||||
// Recurse.
|
||||
_walk(
|
||||
childPath,
|
||||
input.plus(BigNumber.min(remainingInput, fill.input)),
|
||||
output.plus(
|
||||
// Clip the output of the next fill to the remaining
|
||||
// input.
|
||||
clipFillAdjustedOutput(fill, remainingInput),
|
||||
),
|
||||
nextAllFills,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
_walk(bestPath, ZERO_AMOUNT, ZERO_AMOUNT);
|
||||
_walk(bestPath, ZERO_AMOUNT, ZERO_AMOUNT, [...pathA, ...pathB].sort((a, b) => b.rate.comparedTo(a.rate)));
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
@ -77,6 +91,14 @@ function isPathComplete(path: Fill[], targetInput: BigNumber): boolean {
|
||||
return input.gte(targetInput);
|
||||
}
|
||||
|
||||
function clipFillAdjustedOutput(fill: Fill, remainingInput: BigNumber): BigNumber {
|
||||
if (fill.input.lte(remainingInput)) {
|
||||
return fill.adjustedOutput;
|
||||
}
|
||||
const penalty = fill.adjustedOutput.minus(fill.output);
|
||||
return fill.output.times(remainingInput.div(fill.input)).plus(penalty);
|
||||
}
|
||||
|
||||
function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber {
|
||||
if (input.eq(0) || output.eq(0)) {
|
||||
return ZERO_AMOUNT;
|
||||
|
Loading…
x
Reference in New Issue
Block a user