@0x/asset-swapper: Provide more accurate best quote price.

This commit is contained in:
Lawrence Forman 2020-01-16 14:37:15 -05:00 committed by Jacob Evans
parent 160c91f908
commit 09d05d09c9
No known key found for this signature in database
GPG Key ID: 2036DA2ADDFB0842
10 changed files with 376 additions and 247 deletions

View File

@ -17,6 +17,10 @@
{
"note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`",
"pr": 2427
},
{
"note": "Compute more accurate best quote price",
"pr": 2427
}
]
},

View File

@ -54,6 +54,11 @@ export {
SwapQuoteConsumerError,
SignedOrderWithFillableAmounts,
} from './types';
export { ERC20BridgeSource } from './utils/market_operation_utils/types';
export {
ERC20BridgeSource,
CollapsedFill,
NativeCollapsedFill,
OptimizedMarketOrder,
} from './utils/market_operation_utils/types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';

View File

@ -29,9 +29,9 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
excludedSources: [],
bridgeSlippage: 0.0005,
dustFractionThreshold: 0.0025,
numSamples: 12,
numSamples: 13,
noConflicts: true,
sampleDistributionBase: 1.25,
sampleDistributionBase: 1.05,
};
export const constants = {

View File

@ -3,11 +3,17 @@ import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { AbiEncoder, BigNumber } from '@0x/utils';
import { constants } from '../../constants';
import { SignedOrderWithFillableAmounts } from '../../types';
import { sortingUtils } from '../../utils/sorting_utils';
import { constants as marketOperationUtilConstants } from './constants';
import { AggregationError, ERC20BridgeSource, Fill, FillData, NativeFillData, OrderDomain } from './types';
import {
AggregationError,
CollapsedFill,
ERC20BridgeSource,
NativeCollapsedFill,
OptimizedMarketOrder,
OrderDomain,
} from './types';
const { NULL_BYTES, NULL_ADDRESS, ZERO_AMOUNT } = constants;
const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstants;
@ -24,23 +30,21 @@ export class CreateOrderUtils {
orderDomain: OrderDomain,
inputToken: string,
outputToken: string,
path: Fill[],
path: CollapsedFill[],
bridgeSlippage: number,
): SignedOrderWithFillableAmounts[] {
const orders: SignedOrderWithFillableAmounts[] = [];
): OptimizedMarketOrder[] {
const orders: OptimizedMarketOrder[] = [];
for (const fill of path) {
const source = (fill.fillData as FillData).source;
if (source === ERC20BridgeSource.Native) {
orders.push((fill.fillData as NativeFillData).order);
if (fill.source === ERC20BridgeSource.Native) {
orders.push(createNativeOrder(fill));
} else {
orders.push(
createBridgeOrder(
orderDomain,
this._getBridgeAddressFromSource(source),
fill,
this._getBridgeAddressFromSource(fill.source),
outputToken,
inputToken,
fill.output,
fill.input,
bridgeSlippage,
),
);
@ -54,23 +58,21 @@ export class CreateOrderUtils {
orderDomain: OrderDomain,
inputToken: string,
outputToken: string,
path: Fill[],
path: CollapsedFill[],
bridgeSlippage: number,
): SignedOrderWithFillableAmounts[] {
const orders: SignedOrderWithFillableAmounts[] = [];
): OptimizedMarketOrder[] {
const orders: OptimizedMarketOrder[] = [];
for (const fill of path) {
const source = (fill.fillData as FillData).source;
if (source === ERC20BridgeSource.Native) {
orders.push((fill.fillData as NativeFillData).order);
if (fill.source === ERC20BridgeSource.Native) {
orders.push(createNativeOrder(fill));
} else {
orders.push(
createBridgeOrder(
orderDomain,
this._getBridgeAddressFromSource(source),
fill,
this._getBridgeAddressFromSource(fill.source),
inputToken,
outputToken,
fill.input,
fill.output,
bridgeSlippage,
true,
),
@ -97,14 +99,13 @@ export class CreateOrderUtils {
function createBridgeOrder(
orderDomain: OrderDomain,
fill: CollapsedFill,
bridgeAddress: string,
makerToken: string,
takerToken: string,
makerAssetAmount: BigNumber,
takerAssetAmount: BigNumber,
slippage: number,
isBuy: boolean = false,
): SignedOrderWithFillableAmounts {
): OptimizedMarketOrder {
return {
makerAddress: bridgeAddress,
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
@ -113,7 +114,7 @@ function createBridgeOrder(
createBridgeData(takerToken),
),
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken),
...createCommonOrderFields(orderDomain, makerAssetAmount, takerAssetAmount, slippage, isBuy),
...createCommonOrderFields(orderDomain, fill, slippage, isBuy),
};
}
@ -123,24 +124,24 @@ function createBridgeData(tokenAddress: string): string {
}
type CommonOrderFields = Pick<
SignedOrderWithFillableAmounts,
Exclude<keyof SignedOrderWithFillableAmounts, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
OptimizedMarketOrder,
Exclude<keyof OptimizedMarketOrder, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
>;
function createCommonOrderFields(
orderDomain: OrderDomain,
makerAssetAmount: BigNumber,
takerAssetAmount: BigNumber,
fill: CollapsedFill,
slippage: number,
isBuy: boolean = false,
): CommonOrderFields {
const makerAssetAmountAdjustedWithSlippage = isBuy
? makerAssetAmount
: makerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
? fill.totalMakerAssetAmount
: fill.totalMakerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
const takerAssetAmountAdjustedWithSlippage = isBuy
? takerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
: takerAssetAmount;
? fill.totalTakerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
: fill.totalTakerAssetAmount;
return {
fill,
takerAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
feeRecipientAddress: NULL_ADDRESS,
@ -159,3 +160,15 @@ function createCommonOrderFields(
...orderDomain,
};
}
function createNativeOrder(fill: CollapsedFill): OptimizedMarketOrder {
return {
fill: {
source: fill.source,
totalMakerAssetAmount: fill.totalMakerAssetAmount,
totalTakerAssetAmount: fill.totalTakerAssetAmount,
subFills: fill.subFills,
},
...(fill as NativeCollapsedFill).nativeOrder,
};
}

View File

@ -14,12 +14,16 @@ import { comparePathOutputs, FillsOptimizer, getPathOutput } from './fill_optimi
import { DexOrderSampler } from './sampler';
import {
AggregationError,
CollapsedFill,
DexSample,
ERC20BridgeSource,
Fill,
FillData,
FillFlags,
GetMarketOrdersOpts,
NativeCollapsedFill,
NativeFillData,
OptimizedMarketOrder,
OrderDomain,
} from './types';
@ -53,7 +57,7 @@ export class MarketOperationUtils {
nativeOrders: SignedOrder[],
takerAmount: BigNumber,
opts?: Partial<GetMarketOrdersOpts>,
): Promise<SignedOrderWithFillableAmounts[]> {
): Promise<OptimizedMarketOrder[]> {
if (nativeOrders.length === 0) {
throw new Error(AggregationError.EmptyOrders);
}
@ -77,7 +81,7 @@ export class MarketOperationUtils {
takerAmount,
_opts.dustFractionThreshold,
);
const clippedNativePath = clipPathToInput(prunedNativePath, takerAmount);
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath), takerAmount);
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
const allPaths = [...dexPaths];
const allFills = flattenDexPaths(dexPaths);
@ -105,7 +109,7 @@ export class MarketOperationUtils {
this._orderDomain,
inputToken,
outputToken,
simplifyPath(optimalPath),
collapsePath(optimalPath, false),
_opts.bridgeSlippage,
);
}
@ -122,7 +126,7 @@ export class MarketOperationUtils {
nativeOrders: SignedOrder[],
makerAmount: BigNumber,
opts?: Partial<GetMarketOrdersOpts>,
): Promise<SignedOrderWithFillableAmounts[]> {
): Promise<OptimizedMarketOrder[]> {
if (nativeOrders.length === 0) {
throw new Error(AggregationError.EmptyOrders);
}
@ -161,7 +165,7 @@ export class MarketOperationUtils {
batchNativeOrders: SignedOrder[][],
makerAmounts: BigNumber[],
opts?: Partial<GetMarketOrdersOpts>,
): Promise<Array<SignedOrderWithFillableAmounts[] | undefined>> {
): Promise<Array<OptimizedMarketOrder[] | undefined>> {
if (batchNativeOrders.length === 0) {
throw new Error(AggregationError.EmptyOrders);
}
@ -192,7 +196,7 @@ export class MarketOperationUtils {
nativeOrderFillableAmounts: BigNumber[],
dexQuotes: DexSample[][],
opts: GetMarketOrdersOpts,
): SignedOrderWithFillableAmounts[] | undefined {
): OptimizedMarketOrder[] | undefined {
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
nativeOrders,
nativeOrderFillableAmounts,
@ -203,7 +207,7 @@ export class MarketOperationUtils {
makerAmount,
opts.dustFractionThreshold,
);
const clippedNativePath = clipPathToInput(prunedNativePath, makerAmount);
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath).reverse(), makerAmount);
const dexPaths = createPathsFromDexQuotes(dexQuotes, opts.noConflicts);
const allPaths = [...dexPaths];
const allFills = flattenDexPaths(dexPaths);
@ -230,7 +234,7 @@ export class MarketOperationUtils {
this._orderDomain,
inputToken,
outputToken,
simplifyPath(optimalPath),
collapsePath(optimalPath, true),
opts.bridgeSlippage,
);
}
@ -242,26 +246,24 @@ function createSignedOrdersWithFillableAmounts(
operation: MarketOperation,
): SignedOrderWithFillableAmounts[] {
return signedOrders
.map(
(order: SignedOrder, i: number): SignedOrderWithFillableAmounts => {
const fillableAmount = fillableAmounts[i];
const fillableMakerAssetAmount =
operation === MarketOperation.Buy
? fillableAmount
: orderCalculationUtils.getMakerFillAmount(order, fillableAmount);
const fillableTakerAssetAmount =
operation === MarketOperation.Sell
? fillableAmount
: orderCalculationUtils.getTakerFillAmount(order, fillableAmount);
const fillableTakerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, fillableTakerAssetAmount);
return {
fillableMakerAssetAmount,
fillableTakerAssetAmount,
fillableTakerFeeAmount,
...order,
};
},
)
.map((order: SignedOrder, i: number) => {
const fillableAmount = fillableAmounts[i];
const fillableMakerAssetAmount =
operation === MarketOperation.Buy
? fillableAmount
: orderCalculationUtils.getMakerFillAmount(order, fillableAmount);
const fillableTakerAssetAmount =
operation === MarketOperation.Sell
? fillableAmount
: orderCalculationUtils.getTakerFillAmount(order, fillableAmount);
const fillableTakerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, fillableTakerAssetAmount);
return {
fillableMakerAssetAmount,
fillableTakerAssetAmount,
fillableTakerFeeAmount,
...order,
};
})
.filter(order => {
return !order.fillableMakerAssetAmount.isZero() && !order.fillableTakerAssetAmount.isZero();
});
@ -412,23 +414,31 @@ function getPathInput(path: Fill[]): BigNumber {
}
// Merges contiguous fills from the same DEX.
function simplifyPath(path: Fill[]): Fill[] {
const simplified: Fill[] = [];
function collapsePath(path: Fill[], isBuy: boolean): CollapsedFill[] {
const collapsed: Array<CollapsedFill | NativeCollapsedFill> = [];
for (const fill of path) {
const makerAssetAmount = isBuy ? fill.input : fill.output;
const takerAssetAmount = isBuy ? fill.output : fill.input;
const source = (fill.fillData as FillData).source;
if (simplified.length !== 0 && source !== ERC20BridgeSource.Native) {
const prevFill = simplified[simplified.length - 1];
const prevSource = (prevFill.fillData as FillData).source;
if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) {
const prevFill = collapsed[collapsed.length - 1];
// If the last fill is from the same source, merge them.
if (prevSource === source) {
prevFill.input = prevFill.input.plus(fill.input);
prevFill.output = prevFill.output.plus(fill.output);
if (prevFill.source === source) {
prevFill.totalMakerAssetAmount = prevFill.totalMakerAssetAmount.plus(makerAssetAmount);
prevFill.totalTakerAssetAmount = prevFill.totalTakerAssetAmount.plus(takerAssetAmount);
prevFill.subFills.push({ makerAssetAmount, takerAssetAmount });
continue;
}
}
simplified.push(fill);
collapsed.push({
source: fill.fillData.source,
totalMakerAssetAmount: makerAssetAmount,
totalTakerAssetAmount: takerAssetAmount,
subFills: [{ makerAssetAmount, takerAssetAmount }],
nativeOrder: (fill.fillData as NativeFillData).order,
});
}
return simplified;
return collapsed;
}
// Sort fills by descending price.

View File

@ -79,6 +79,48 @@ export interface Fill {
fillData: FillData | NativeFillData;
}
/**
* Represents continguous fills on a path that have been merged together.
*/
export interface CollapsedFill {
/**
* The source DEX.
*/
source: ERC20BridgeSource;
/**
* Total maker asset amount.
*/
totalMakerAssetAmount: BigNumber;
/**
* Total taker asset amount.
*/
totalTakerAssetAmount: BigNumber;
/**
* All the fill asset amounts that were collapsed into this node.
*/
subFills: Array<{
makerAssetAmount: BigNumber;
takerAssetAmount: BigNumber;
}>;
}
/**
* A `CollapsedFill` wrapping a native order.
*/
export interface NativeCollapsedFill extends CollapsedFill {
nativeOrder: SignedOrderWithFillableAmounts;
}
/**
* Optimized orders to fill.
*/
export interface OptimizedMarketOrder extends SignedOrderWithFillableAmounts {
/**
* The optimized fills that generated this order.
*/
fill: CollapsedFill;
}
/**
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
*/

View File

@ -16,6 +16,7 @@ import {
import { fillableAmountsUtils } from './fillable_amounts_utils';
import { MarketOperationUtils } from './market_operation_utils';
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
import { ProtocolFeeUtils } from './protocol_fee_utils';
import { utils } from './utils';
@ -79,6 +80,7 @@ export class SwapQuoteCalculator {
opts,
)) as Array<MarketBuySwapQuote | undefined>;
}
private async _calculateBatchBuySwapQuoteAsync(
batchPrunedOrders: SignedOrder[][],
assetFillAmounts: BigNumber[],
@ -106,7 +108,6 @@ export class SwapQuoteCalculator {
operation,
assetFillAmounts[i],
gasPrice,
opts,
);
} else {
return undefined;
@ -126,7 +127,7 @@ export class SwapQuoteCalculator {
// since prunedOrders do not have fillState, we will add a buffer of fillable orders to consider that some native are orders are partially filled
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
let resultOrders: SignedOrderWithFillableAmounts[] = [];
let resultOrders: OptimizedMarketOrder[] = [];
if (operation === MarketOperation.Buy) {
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
@ -151,37 +152,35 @@ export class SwapQuoteCalculator {
operation,
assetFillAmount,
gasPrice,
opts,
);
}
private async _createSwapQuoteAsync(
makerAssetData: string,
takerAssetData: string,
resultOrders: SignedOrderWithFillableAmounts[],
resultOrders: OptimizedMarketOrder[],
operation: MarketOperation,
assetFillAmount: BigNumber,
gasPrice: BigNumber,
opts: CalculateSwapQuoteOpts,
): Promise<SwapQuote> {
const bestCaseQuoteInfo = await this._calculateQuoteInfoAsync(
createBestCaseOrders(resultOrders, operation, opts.bridgeSlippage),
resultOrders,
assetFillAmount,
gasPrice,
operation,
);
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts
// such that they are sorted from worst rate to best rate
const worstCaseQuoteInfo = await this._calculateQuoteInfoAsync(
_.reverse(resultOrders.slice()),
resultOrders,
assetFillAmount,
gasPrice,
operation,
true,
);
const quoteBase = {
takerAssetData,
makerAssetData,
orders: resultOrders,
// Remove fill metadata.
orders: resultOrders.map(o => _.omit(o, 'fill')) as SignedOrderWithFillableAmounts[],
bestCaseQuoteInfo,
worstCaseQuoteInfo,
gasPrice,
@ -204,31 +203,39 @@ export class SwapQuoteCalculator {
// tslint:disable-next-line: prefer-function-over-method
private async _calculateQuoteInfoAsync(
prunedOrders: SignedOrderWithFillableAmounts[],
orders: OptimizedMarketOrder[],
assetFillAmount: BigNumber,
gasPrice: BigNumber,
operation: MarketOperation,
worstCase: boolean = false,
): Promise<SwapQuoteInfo> {
if (operation === MarketOperation.Buy) {
return this._calculateMarketBuyQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
return this._calculateMarketBuyQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
} else {
return this._calculateMarketSellQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
return this._calculateMarketSellQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
}
}
private async _calculateMarketSellQuoteInfoAsync(
prunedOrders: SignedOrderWithFillableAmounts[],
orders: OptimizedMarketOrder[],
takerAssetSellAmount: BigNumber,
gasPrice: BigNumber,
worstCase: boolean = false,
): Promise<SwapQuoteInfo> {
const result = prunedOrders.reduce(
(acc, order) => {
const {
totalMakerAssetAmount,
totalTakerAssetAmount,
totalFeeTakerAssetAmount,
remainingTakerAssetFillAmount,
} = acc;
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
let remainingTakerAssetFillAmount = takerAssetSellAmount;
const filledOrders = [] as OptimizedMarketOrder[];
const _orders = !worstCase ? orders : orders.slice().reverse();
for (const order of _orders) {
let makerAssetAmount = constants.ZERO_AMOUNT;
let takerAssetAmount = constants.ZERO_AMOUNT;
let feeTakerAssetAmount = constants.ZERO_AMOUNT;
if (remainingTakerAssetFillAmount.lte(0)) {
break;
}
if (order.fill.source === ERC20BridgeSource.Native) {
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
order,
);
@ -239,99 +246,155 @@ export class SwapQuoteCalculator {
remainingTakerAssetFillAmount,
adjustedFillableTakerAssetAmount,
);
const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown(
order,
takerAssetAmountWithFees,
);
const makerAssetAmount = takerAssetAmountWithFees
const takerAssetAmountBreakDown = getTakerAssetAmountBreakDown(order, takerAssetAmountWithFees);
takerAssetAmount = takerAssetAmountBreakDown.takerAssetAmount;
feeTakerAssetAmount = takerAssetAmountBreakDown.feeTakerAssetAmount;
makerAssetAmount = takerAssetAmountWithFees
.div(adjustedFillableTakerAssetAmount)
.multipliedBy(adjustedFillableMakerAssetAmount)
.times(adjustedFillableMakerAssetAmount)
.integerValue(BigNumber.ROUND_DOWN);
return {
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerAssetAmount),
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
remainingTakerAssetFillAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingTakerAssetFillAmount.minus(takerAssetAmountWithFees),
),
};
},
{
totalMakerAssetAmount: constants.ZERO_AMOUNT,
totalTakerAssetAmount: constants.ZERO_AMOUNT,
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
remainingTakerAssetFillAmount: takerAssetSellAmount,
},
);
} else {
// This is a collapsed bridge order.
// Because collapsed bridge orders actually fill at different rates,
// we can iterate over the uncollapsed fills to get the actual
// asset amounts transfered.
// We can also assume there are no fees and the order is not
// partially filled.
// Infer the bridge slippage from the difference between the fill
// size and the atual order asset amounts.
const makerAssetBridgeSlippage = !worstCase
? constants.ONE_AMOUNT
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
const takerAssetBridgeSlippage = !worstCase
? constants.ONE_AMOUNT
: order.takerAssetAmount.div(order.fill.totalTakerAssetAmount);
// Consecutively fill the subfills in this order.
const subFills = !worstCase ? order.fill.subFills : order.fill.subFills.slice().reverse();
for (const subFill of subFills) {
if (remainingTakerAssetFillAmount.minus(takerAssetAmount).lte(0)) {
break;
}
const partialTakerAssetAmount = subFill.takerAssetAmount.times(takerAssetBridgeSlippage);
const partialMakerAssetAmount = subFill.makerAssetAmount.times(makerAssetBridgeSlippage);
const partialTakerAssetFillAmount = BigNumber.min(
partialTakerAssetAmount,
remainingTakerAssetFillAmount.minus(takerAssetAmount),
);
const partialMakerAssetFillAmount = partialTakerAssetFillAmount
.div(partialTakerAssetAmount)
.times(partialMakerAssetAmount)
.integerValue(BigNumber.ROUND_DOWN);
takerAssetAmount = takerAssetAmount.plus(partialTakerAssetFillAmount);
makerAssetAmount = makerAssetAmount.plus(partialMakerAssetFillAmount);
}
}
totalMakerAssetAmount = totalMakerAssetAmount.plus(makerAssetAmount);
totalTakerAssetAmount = totalTakerAssetAmount.plus(takerAssetAmount);
totalFeeTakerAssetAmount = totalFeeTakerAssetAmount.plus(feeTakerAssetAmount);
remainingTakerAssetFillAmount = remainingTakerAssetFillAmount
.minus(takerAssetAmount)
.minus(feeTakerAssetAmount);
filledOrders.push(order);
}
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
prunedOrders,
filledOrders,
gasPrice,
);
return {
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
takerAssetAmount: result.totalTakerAssetAmount,
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
makerAssetAmount: result.totalMakerAssetAmount,
feeTakerAssetAmount: totalFeeTakerAssetAmount,
takerAssetAmount: totalTakerAssetAmount,
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
makerAssetAmount: totalMakerAssetAmount,
protocolFeeInWeiAmount,
};
}
private async _calculateMarketBuyQuoteInfoAsync(
prunedOrders: SignedOrderWithFillableAmounts[],
orders: OptimizedMarketOrder[],
makerAssetBuyAmount: BigNumber,
gasPrice: BigNumber,
worstCase: boolean = false,
): Promise<SwapQuoteInfo> {
const result = prunedOrders.reduce(
(acc, order) => {
const {
totalMakerAssetAmount,
totalTakerAssetAmount,
totalFeeTakerAssetAmount,
remainingMakerAssetFillAmount,
} = acc;
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
let remainingMakerAssetFillAmount = makerAssetBuyAmount;
const filledOrders = [] as OptimizedMarketOrder[];
const _orders = !worstCase ? orders : orders.slice().reverse();
for (const order of _orders) {
let makerAssetAmount = constants.ZERO_AMOUNT;
let takerAssetAmount = constants.ZERO_AMOUNT;
let feeTakerAssetAmount = constants.ZERO_AMOUNT;
if (remainingMakerAssetFillAmount.lte(0)) {
break;
}
if (order.fill.source === ERC20BridgeSource.Native) {
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
order,
);
const adjustedFillableTakerAssetAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterFees(
order,
);
const makerFillAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
const takerAssetAmountWithFees = makerFillAmount
makerAssetAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
const takerAssetAmountWithFees = makerAssetAmount
.div(adjustedFillableMakerAssetAmount)
.multipliedBy(adjustedFillableTakerAssetAmount)
.integerValue(BigNumber.ROUND_UP);
const takerAssetAmountBreakDown = getTakerAssetAmountBreakDown(order, takerAssetAmountWithFees);
takerAssetAmount = takerAssetAmountBreakDown.takerAssetAmount;
feeTakerAssetAmount = takerAssetAmountBreakDown.feeTakerAssetAmount;
} else {
// This is a collapsed bridge order.
// Because collapsed bridge orders actually fill at different rates,
// we can iterate over the uncollapsed fills to get the actual
// asset amounts transfered.
// We can also assume there are no fees and the order is not
// partially filled.
const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown(
order,
takerAssetAmountWithFees,
);
return {
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerFillAmount),
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
remainingMakerAssetFillAmount: BigNumber.max(
constants.ZERO_AMOUNT,
remainingMakerAssetFillAmount.minus(makerFillAmount),
),
};
},
{
totalMakerAssetAmount: constants.ZERO_AMOUNT,
totalTakerAssetAmount: constants.ZERO_AMOUNT,
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
remainingMakerAssetFillAmount: makerAssetBuyAmount,
},
);
// Infer the bridge slippage from the difference between the fill
// size and the atual order asset amounts.
const makerAssetBridgeSlippage = !worstCase
? constants.ONE_AMOUNT
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
const takerAssetBridgeSlippage = !worstCase
? constants.ONE_AMOUNT
: order.takerAssetAmount.div(order.fill.totalTakerAssetAmount);
// Consecutively fill the subfills in this order.
const subFills = !worstCase ? order.fill.subFills : order.fill.subFills.slice().reverse();
for (const subFill of subFills) {
if (remainingMakerAssetFillAmount.minus(makerAssetAmount).lte(0)) {
break;
}
const partialTakerAssetAmount = subFill.takerAssetAmount.times(takerAssetBridgeSlippage);
const partialMakerAssetAmount = subFill.makerAssetAmount.times(makerAssetBridgeSlippage);
const partialMakerAssetFillAmount = BigNumber.min(
partialMakerAssetAmount,
remainingMakerAssetFillAmount.minus(makerAssetAmount),
);
const partialTakerAssetFillAmount = partialMakerAssetFillAmount
.div(partialMakerAssetAmount)
.times(partialTakerAssetAmount)
.integerValue(BigNumber.ROUND_UP);
takerAssetAmount = takerAssetAmount.plus(partialTakerAssetFillAmount);
makerAssetAmount = makerAssetAmount.plus(partialMakerAssetFillAmount);
}
}
totalMakerAssetAmount = totalMakerAssetAmount.plus(makerAssetAmount);
totalTakerAssetAmount = totalTakerAssetAmount.plus(takerAssetAmount);
totalFeeTakerAssetAmount = totalFeeTakerAssetAmount.plus(feeTakerAssetAmount);
remainingMakerAssetFillAmount = remainingMakerAssetFillAmount.minus(makerAssetAmount);
filledOrders.push(order);
}
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
prunedOrders,
filledOrders,
gasPrice,
);
return {
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
takerAssetAmount: result.totalTakerAssetAmount,
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
makerAssetAmount: result.totalMakerAssetAmount,
feeTakerAssetAmount: totalFeeTakerAssetAmount,
takerAssetAmount: totalTakerAssetAmount,
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
makerAssetAmount: totalMakerAssetAmount,
protocolFeeInWeiAmount,
};
}
@ -372,35 +435,3 @@ function getTakerAssetAmountBreakDown(
takerAssetAmount: takerAssetAmountWithFees,
};
}
function createBestCaseOrders(
orders: SignedOrderWithFillableAmounts[],
operation: MarketOperation,
bridgeSlippage: number,
): SignedOrderWithFillableAmounts[] {
// Scale the asset amounts to undo the bridge slippage applied to
// bridge orders.
const bestCaseOrders: SignedOrderWithFillableAmounts[] = [];
for (const order of orders) {
if (!order.makerAssetData.startsWith(constants.BRIDGE_ASSET_DATA_PREFIX)) {
bestCaseOrders.push(order);
continue;
}
bestCaseOrders.push({
...order,
...(operation === MarketOperation.Sell
? {
makerAssetAmount: order.makerAssetAmount
.dividedBy(1 - bridgeSlippage)
.integerValue(BigNumber.ROUND_DOWN),
}
: // Buy operation
{
takerAssetAmount: order.takerAssetAmount
.dividedBy(bridgeSlippage + 1)
.integerValue(BigNumber.ROUND_UP),
}),
});
}
return bestCaseOrders;
}

View File

@ -23,7 +23,7 @@ describe('#calculateLiquidity', () => {
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
prunedSignedOrders,
);
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9));
});
it('should provide correct liquidity result with orders with takerFees in takerAsset', () => {
@ -31,7 +31,7 @@ describe('#calculateLiquidity', () => {
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
prunedSignedOrders,
);
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15));
});
it('should provide correct liquidity result with orders with takerFees in makerAsset', () => {
@ -51,7 +51,7 @@ describe('#calculateLiquidity', () => {
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
prunedSignedOrders,
);
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(25));
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(27));
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33));
});
});

View File

@ -65,6 +65,9 @@ const createSamplerFromSignedOrdersWithFillableAmounts = (
return sampler;
};
// TODO(dorothy-zbornak): Replace these tests entirely with unit tests because
// omg they're a nightmare to maintain.
// tslint:disable:max-file-line-count
// tslint:disable:custom-no-magic-numbers
describe('swapQuoteCalculator', () => {
@ -272,7 +275,7 @@ describe('swapQuoteCalculator', () => {
});
});
it('calculates a correct swapQuote with slippage (feeless orders)', async () => {
const assetSellAmount = baseUnitAmount(1);
const assetSellAmount = baseUnitAmount(4);
const slippagePercentage = 0.2;
const sampler = createSamplerFromSignedOrdersWithFillableAmounts(
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS,
@ -289,10 +292,10 @@ describe('swapQuoteCalculator', () => {
GAS_PRICE,
CALCULATE_SWAP_QUOTE_OPTS,
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[2],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
]);
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
@ -301,15 +304,15 @@ describe('swapQuoteCalculator', () => {
feeTakerAssetAmount: baseUnitAmount(0),
takerAssetAmount: assetSellAmount,
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(6),
makerAssetAmount: baseUnitAmount(9),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(0),
takerAssetAmount: assetSellAmount,
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(0.4),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
makerAssetAmount: baseUnitAmount(1.6),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
});
it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', async () => {
@ -372,7 +375,7 @@ describe('swapQuoteCalculator', () => {
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[2],
]);
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
// test if rates are correct
@ -381,14 +384,14 @@ describe('swapQuoteCalculator', () => {
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(4.5),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(0.5),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(0.5)),
feeTakerAssetAmount: baseUnitAmount(1.2),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.2)),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(1),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
makerAssetAmount: baseUnitAmount(1.8),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
});
it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', async () => {
@ -411,23 +414,24 @@ describe('swapQuoteCalculator', () => {
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
]);
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
// test if rates are correct
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(0.8),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
makerAssetAmount: baseUnitAmount(4),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(0.8),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
makerAssetAmount: baseUnitAmount(4),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
});
it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', async () => {
@ -451,24 +455,24 @@ describe('swapQuoteCalculator', () => {
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
]);
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
// test if rates are correct
// 50 takerAsset units to fill the first order + 100 takerAsset units for fees
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(4),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(0.8),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2),
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
totalTakerAssetAmount: assetSellAmount,
makerAssetAmount: baseUnitAmount(3.6),
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
});
});
@ -678,7 +682,7 @@ describe('swapQuoteCalculator', () => {
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[2],
]);
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
@ -692,12 +696,16 @@ describe('swapQuoteCalculator', () => {
takerAssetAmount,
totalTakerAssetAmount: takerAssetAmount,
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(0),
takerAssetAmount: baseUnitAmount(5.5),
totalTakerAssetAmount: baseUnitAmount(5.5),
takerAssetAmount: baseUnitAmount(20)
.div(6)
.integerValue(BigNumber.ROUND_UP),
totalTakerAssetAmount: baseUnitAmount(20)
.div(6)
.integerValue(BigNumber.ROUND_UP),
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
@ -766,7 +774,7 @@ describe('swapQuoteCalculator', () => {
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[2],
]);
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
// test if rates are correct
@ -775,12 +783,16 @@ describe('swapQuoteCalculator', () => {
takerAssetAmount: fiveSixthEthInWei,
totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei),
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2.5),
takerAssetAmount: baseUnitAmount(5.5),
totalTakerAssetAmount: baseUnitAmount(8),
feeTakerAssetAmount: baseUnitAmount(3),
takerAssetAmount: baseUnitAmount(10)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 3.3333...
totalTakerAssetAmount: baseUnitAmount(19)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 6.3333...
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
@ -805,21 +817,33 @@ describe('swapQuoteCalculator', () => {
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
]);
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
// test if rates are correct
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2.5),
takerAssetAmount: baseUnitAmount(2.5),
totalTakerAssetAmount: baseUnitAmount(5),
feeTakerAssetAmount: baseUnitAmount(0.5)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.16666...
takerAssetAmount: baseUnitAmount(0.5)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.1666...
totalTakerAssetAmount: baseUnitAmount(1)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.3333...
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2.5),
takerAssetAmount: baseUnitAmount(2.5),
totalTakerAssetAmount: baseUnitAmount(5),
feeTakerAssetAmount: baseUnitAmount(0.5)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.16666...
takerAssetAmount: baseUnitAmount(0.5)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.1666...
totalTakerAssetAmount: baseUnitAmount(1)
.div(3)
.integerValue(BigNumber.ROUND_UP), // 0.3333...
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
@ -845,14 +869,14 @@ describe('swapQuoteCalculator', () => {
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
]);
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
// test if rates are correct
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
feeTakerAssetAmount: baseUnitAmount(2.75),
takerAssetAmount: baseUnitAmount(2.75),
totalTakerAssetAmount: baseUnitAmount(5.5),
feeTakerAssetAmount: baseUnitAmount(1.25).minus(1),
takerAssetAmount: baseUnitAmount(2.25).plus(1),
totalTakerAssetAmount: baseUnitAmount(3.5),
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
});
@ -879,7 +903,7 @@ describe('swapQuoteCalculator', () => {
.multipliedBy(0.1)
.integerValue(BigNumber.ROUND_CEIL),
makerAssetAmount: assetBuyAmount,
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
});
});
});

View File

@ -51,7 +51,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS: Array<Partial<SignedOrderWit
takerAssetAmount: baseUnitAmount(6),
makerAssetAmount: baseUnitAmount(6),
fillableTakerAssetAmount: baseUnitAmount(3),
fillableMakerAssetAmount: baseUnitAmount(2),
fillableMakerAssetAmount: baseUnitAmount(3),
},
...PARTIAL_ORDER,
},
@ -86,7 +86,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET: Array<Partial<Sig
makerAssetAmount: baseUnitAmount(6),
takerFee: baseUnitAmount(4),
fillableTakerAssetAmount: baseUnitAmount(3),
fillableMakerAssetAmount: baseUnitAmount(2),
fillableMakerAssetAmount: baseUnitAmount(3),
fillableTakerFeeAmount: baseUnitAmount(2),
},
...PARTIAL_ORDER_FEE_IN_TAKER_ASSET,