Fix/routing glue optimization (#439)

* fix: only create fills if using js router, remove `unoptimizedPath`

* minor optimizations

* remove the cap of route output in buys

* change PR number

* fix: Update Ropsten UniswapV3 Router address (#441)

* fix: Update Ropsten UniswapV3 Router address

* Update CHANGELOG

* Updated CHANGELOGS & MD docs

* Publish

 - @0x/asset-swapper@16.50.2

* change PR number

Co-authored-by: Kim Persson <kimpersson88@gmail.com>
Co-authored-by: Jacob Evans <jacob@dekz.net>
Co-authored-by: Github Actions <github-actions@github.com>
This commit is contained in:
Shawn 2022-03-08 16:26:22 -08:00 committed by GitHub
parent b3ee294ba5
commit f6e85aedf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 29 additions and 34 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "16.50.3",
"changes": [
{
"note": "Routing glue optimization",
"pr": 439
}
]
},
{ {
"version": "16.50.2", "version": "16.50.2",
"changes": [ "changes": [

View File

@ -175,9 +175,9 @@ export function dexSamplesToFills(
const { source, fillData } = sample; const { source, fillData } = sample;
const input = sample.input.minus(prevSample ? prevSample.input : 0); const input = sample.input.minus(prevSample ? prevSample.input : 0);
const output = sample.output.minus(prevSample ? prevSample.output : 0); const output = sample.output.minus(prevSample ? prevSample.output : 0);
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
let penalty = ZERO_AMOUNT; let penalty = ZERO_AMOUNT;
if (i === 0) { if (i === 0) {
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
// Only the first fill in a DEX path incurs a penalty. // Only the first fill in a DEX path incurs a penalty.
penalty = ethToOutputAmount({ penalty = ethToOutputAmount({
input, input,

View File

@ -42,7 +42,7 @@ import { createFills } from './fills';
import { getBestTwoHopQuote } from './multihop_utils'; import { getBestTwoHopQuote } from './multihop_utils';
import { createOrdersFromTwoHopSample } from './orders'; import { createOrdersFromTwoHopSample } from './orders';
import { Path, PathPenaltyOpts } from './path'; import { Path, PathPenaltyOpts } from './path';
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer'; import { findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler'; import { DexOrderSampler, getSampleAmounts } from './sampler';
import { SourceFilters } from './source_filters'; import { SourceFilters } from './source_filters';
import { import {
@ -493,18 +493,6 @@ export class MarketOperationUtils {
} as NativeOrderWithFillableAmounts), } as NativeOrderWithFillableAmounts),
); );
// Convert native orders and dex quotes into `Fill` objects.
const fills = createFills({
side,
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
dexQuotes,
targetInput: inputAmount,
outputAmountPerEth,
inputAmountPerEth,
excludedSources: opts.excludedSources,
feeSchedule: opts.feeSchedule,
});
// Find the optimal path. // Find the optimal path.
const penaltyOpts: PathPenaltyOpts = { const penaltyOpts: PathPenaltyOpts = {
outputAmountPerEth, outputAmountPerEth,
@ -517,13 +505,11 @@ export class MarketOperationUtils {
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth; const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth; const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
// Find the unoptimized best rate to calculate savings from optimizer let fills: Fill[][];
const _unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, penaltyOpts)[0];
const unoptimizedPath = _unoptimizedPath ? _unoptimizedPath.collapse(orderOpts) : undefined;
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router // Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
let optimalPath: Path | undefined; let optimalPath: Path | undefined;
if (SHOULD_USE_RUST_ROUTER) { if (SHOULD_USE_RUST_ROUTER) {
fills = [[]];
optimalPath = findOptimalRustPathFromSamples( optimalPath = findOptimalRustPathFromSamples(
side, side,
dexQuotes, dexQuotes,
@ -536,6 +522,18 @@ export class MarketOperationUtils {
opts.samplerMetrics, opts.samplerMetrics,
); );
} else { } else {
// Convert native orders and dex quotes into `Fill` objects.
fills = createFills({
side,
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
dexQuotes,
targetInput: inputAmount,
outputAmountPerEth,
inputAmountPerEth,
excludedSources: opts.excludedSources,
feeSchedule: opts.feeSchedule,
});
optimalPath = await findOptimalPathJSAsync( optimalPath = await findOptimalPathJSAsync(
side, side,
fills, fills,
@ -561,7 +559,6 @@ export class MarketOperationUtils {
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop], sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
marketSideLiquidity, marketSideLiquidity,
adjustedRate: bestTwoHopRate, adjustedRate: bestTwoHopRate,
unoptimizedPath,
takerAmountPerEth, takerAmountPerEth,
makerAmountPerEth, makerAmountPerEth,
}; };
@ -582,7 +579,6 @@ export class MarketOperationUtils {
sourceFlags: collapsedPath.sourceFlags, sourceFlags: collapsedPath.sourceFlags,
marketSideLiquidity, marketSideLiquidity,
adjustedRate: optimalPathRate, adjustedRate: optimalPathRate,
unoptimizedPath,
takerAmountPerEth, takerAmountPerEth,
makerAmountPerEth, makerAmountPerEth,
}; };

View File

@ -122,17 +122,8 @@ export class Path {
++i; ++i;
continue; continue;
} }
// If there are contiguous bridge orders, we can batch them together.
// TODO jacob pretty sure this is from DFB and we can remove
const contiguousBridgeFills = [collapsedFills[i]];
for (let j = i + 1; j < collapsedFills.length; ++j) {
if (collapsedFills[j].source === ERC20BridgeSource.Native) {
break;
}
contiguousBridgeFills.push(collapsedFills[j]);
}
this.orders.push(createBridgeOrder(contiguousBridgeFills[0], makerToken, takerToken, opts.side)); this.orders.push(createBridgeOrder(collapsedFills[i], makerToken, takerToken, opts.side));
i += 1; i += 1;
} }
return this as CollapsedPath; return this as CollapsedPath;

View File

@ -303,7 +303,7 @@ function findRoutesAndCreateOptimalPath(
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router // TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output)); const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output));
// Scale output by scale factor but never go above the largest sample (unknown liquidity) or below 1 base unit (unfillable) // Scale output by scale factor but never go above the largest sample in sell quotes (unknown liquidity) or below 1 base unit (unfillable)
const scaleOutput = (output: BigNumber) => { const scaleOutput = (output: BigNumber) => {
// Don't try to scale 0 output as it will be clamped to 1 // Don't try to scale 0 output as it will be clamped to 1
if (output.eq(ZERO_AMOUNT)) { if (output.eq(ZERO_AMOUNT)) {
@ -313,8 +313,9 @@ function findRoutesAndCreateOptimalPath(
const scaled = output const scaled = output
.times(scale) .times(scale)
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL); .decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
const capped = MarketOperation.Sell ? BigNumber.min(scaled, maxSampledOutput) : scaled;
return BigNumber.max(BigNumber.min(scaled, maxSampledOutput), 1); return BigNumber.max(capped, 1);
}; };
adjustedFills.push({ adjustedFills.push({

View File

@ -10,7 +10,6 @@ import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts }
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor'; import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor';
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator'; import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
import { CollapsedPath } from './path';
import { SourceFilters } from './source_filters'; import { SourceFilters } from './source_filters';
/** /**
@ -579,7 +578,6 @@ export interface OptimizerResult {
liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>; liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
marketSideLiquidity: MarketSideLiquidity; marketSideLiquidity: MarketSideLiquidity;
adjustedRate: BigNumber; adjustedRate: BigNumber;
unoptimizedPath?: CollapsedPath;
takerAmountPerEth: BigNumber; takerAmountPerEth: BigNumber;
makerAmountPerEth: BigNumber; makerAmountPerEth: BigNumber;
} }