Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return unoptimized quote in SwapQuote #62

Merged
merged 2 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/asset-swapper/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export {
export { artifacts } from './artifacts';
export { InsufficientAssetLiquidityError } from './errors';
export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer';
export { getSwapMinBuyAmount } from './quote_consumers/utils';
export { getSwapMinBuyAmount, getQuoteInfoMinBuyAmount } from './quote_consumers/utils';
export { SwapQuoter } from './swap_quoter';
export {
AffiliateFee,
Expand Down
35 changes: 33 additions & 2 deletions packages/asset-swapper/src/quote_consumers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';

import { MarketOperation, SwapQuote } from '../types';
import { ERC20BridgeSource } from '../utils/market_operation_utils/types';
import { MarketOperation, SwapQuote, SwapQuoteInfo } from '../types';
import { ERC20BridgeSource, OptimizedMarketOrder } from '../utils/market_operation_utils/types';

/**
* Compute the minimum buy token amount for market operations by inferring
Expand Down Expand Up @@ -31,3 +31,34 @@ export function getSwapMinBuyAmount(quote: SwapQuote): BigNumber {
}
return quote.bestCaseQuoteInfo.makerAssetAmount.times(slipRatio).integerValue(BigNumber.ROUND_DOWN);
}

/**
* Same as `getSwapMinBuyAmount` but operates
* on a single quote info instead of using best and worst case
* Orders must be derived from the same path as the quote info
*/
export function getQuoteInfoMinBuyAmount(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use this in 0x-api to calculate affiliate fees if any

quoteInfo: SwapQuoteInfo,
orders: OptimizedMarketOrder[],
marketOperation: MarketOperation,
): BigNumber {
if (marketOperation === MarketOperation.Buy) {
return quoteInfo.makerAssetAmount;
}
let slipRatio = new BigNumber(1);
// Infer the allowed maker asset slippage from any non-native order.
for (const o of orders) {
if (o.fills.length === 0 || o.fills[0].source === ERC20BridgeSource.Native) {
// No slippage on native orders.
continue;
}
const totalFillMakerAssetAmount = BigNumber.sum(...o.fills.map(f => f.output));
slipRatio = o.fillableMakerAssetAmount.div(totalFillMakerAssetAmount);
break;
}
if (slipRatio.gte(1)) {
// No slippage allowed across all orders.
return quoteInfo.makerAssetAmount;
}
return quoteInfo.makerAssetAmount.times(slipRatio).integerValue(BigNumber.ROUND_DOWN);
}
2 changes: 2 additions & 0 deletions packages/asset-swapper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ export interface SwapQuoteBase {
worstCaseQuoteInfo: SwapQuoteInfo;
sourceBreakdown: SwapQuoteOrdersBreakdown;
quoteReport?: QuoteReport;
unoptimizedQuoteInfo: SwapQuoteInfo;
unoptimizedOrders: OptimizedMarketOrder[];
isTwoHop: boolean;
makerTokenDecimals: number;
takerTokenDecimals: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
createSignedOrdersWithFillableAmounts,
getNativeOrderTokens,
} from './orders';
import { findOptimalPathAsync } from './path_optimizer';
import { fillsToSortedPaths, findOptimalPathAsync } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler';
import { SourceFilters } from './source_filters';
import {
Expand Down Expand Up @@ -543,6 +543,10 @@ export class MarketOperationUtils {
exchangeProxyOverhead: opts.exchangeProxyOverhead || (() => ZERO_AMOUNT),
};

// Find the unoptimized best rate to calculate savings from optimizer
const unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, optimizerOpts)[0].collapse(orderOpts);

// Find the optimal path
const optimalPath = await findOptimalPathAsync(side, fills, inputAmount, opts.runLimit, optimizerOpts);
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;

Expand All @@ -559,6 +563,7 @@ export class MarketOperationUtils {
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
marketSideLiquidity,
adjustedRate: bestTwoHopRate,
unoptimizedPath,
};
}

Expand Down Expand Up @@ -591,6 +596,7 @@ export class MarketOperationUtils {
sourceFlags: collapsedPath.sourceFlags,
marketSideLiquidity,
adjustedRate: optimalPathRate,
unoptimizedPath,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ export async function findOptimalPathAsync(
runLimit: number = 2 ** 8,
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
): Promise<Path | undefined> {
const rates = rateBySourcePathId(side, fills, targetInput);
const paths = fills.map(singleSourceFills => Path.create(side, singleSourceFills, targetInput, opts));
// Sort fill arrays by descending adjusted completed rate.
const sortedPaths = paths.sort((a, b) => b.adjustedCompleteRate().comparedTo(a.adjustedCompleteRate()));
const sortedPaths = fillsToSortedPaths(fills, side, targetInput, opts);
if (sortedPaths.length === 0) {
return undefined;
}
let optimalPath = sortedPaths[0];
const rates = rateBySourcePathId(side, fills, targetInput);
for (const [i, path] of sortedPaths.slice(1).entries()) {
optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit * RUN_LIMIT_DECAY_FACTOR ** i, rates);
// Yield to event loop.
Expand All @@ -37,6 +36,18 @@ export async function findOptimalPathAsync(
return optimalPath.isComplete() ? optimalPath : undefined;
}

// Sort fill arrays by descending adjusted completed rate.
export function fillsToSortedPaths(
fills: Fill[][],
side: MarketOperation,
targetInput: BigNumber,
opts: PathPenaltyOpts,
): Path[] {
const paths = fills.map(singleSourceFills => Path.create(side, singleSourceFills, targetInput, opts));
const sortedPaths = paths.sort((a, b) => b.adjustedCompleteRate().comparedTo(a.adjustedCompleteRate()));
return sortedPaths;
}

function mixPaths(
side: MarketOperation,
pathA: Path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RfqtFirmQuoteValidator, RfqtRequestOpts, SignedOrderWithFillableAmounts
import { QuoteRequestor } from '../../utils/quote_requestor';
import { QuoteReport } from '../quote_report_generator';

import { CollapsedPath } from './path';
import { SourceFilters } from './source_filters';

/**
Expand Down Expand Up @@ -342,6 +343,7 @@ export interface OptimizerResult {
liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
marketSideLiquidity: MarketSideLiquidity;
adjustedRate: BigNumber;
unoptimizedPath: CollapsedPath;
}

export interface OptimizerResultWithReport extends OptimizerResult {
Expand Down
Loading