@0x/asset-swapper
: Provide more accurate best quote price.
This commit is contained in:
committed by
Jacob Evans
parent
160c91f908
commit
09d05d09c9
@@ -17,6 +17,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`",
|
"note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`",
|
||||||
"pr": 2427
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compute more accurate best quote price",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@@ -54,6 +54,11 @@ export {
|
|||||||
SwapQuoteConsumerError,
|
SwapQuoteConsumerError,
|
||||||
SignedOrderWithFillableAmounts,
|
SignedOrderWithFillableAmounts,
|
||||||
} from './types';
|
} 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 { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
|
@@ -29,9 +29,9 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
|||||||
excludedSources: [],
|
excludedSources: [],
|
||||||
bridgeSlippage: 0.0005,
|
bridgeSlippage: 0.0005,
|
||||||
dustFractionThreshold: 0.0025,
|
dustFractionThreshold: 0.0025,
|
||||||
numSamples: 12,
|
numSamples: 13,
|
||||||
noConflicts: true,
|
noConflicts: true,
|
||||||
sampleDistributionBase: 1.25,
|
sampleDistributionBase: 1.05,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
|
@@ -3,11 +3,17 @@ import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
|||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { constants } from '../../constants';
|
import { constants } from '../../constants';
|
||||||
import { SignedOrderWithFillableAmounts } from '../../types';
|
|
||||||
import { sortingUtils } from '../../utils/sorting_utils';
|
import { sortingUtils } from '../../utils/sorting_utils';
|
||||||
|
|
||||||
import { constants as marketOperationUtilConstants } from './constants';
|
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 { NULL_BYTES, NULL_ADDRESS, ZERO_AMOUNT } = constants;
|
||||||
const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstants;
|
const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstants;
|
||||||
@@ -24,23 +30,21 @@ export class CreateOrderUtils {
|
|||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
inputToken: string,
|
inputToken: string,
|
||||||
outputToken: string,
|
outputToken: string,
|
||||||
path: Fill[],
|
path: CollapsedFill[],
|
||||||
bridgeSlippage: number,
|
bridgeSlippage: number,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): OptimizedMarketOrder[] {
|
||||||
const orders: SignedOrderWithFillableAmounts[] = [];
|
const orders: OptimizedMarketOrder[] = [];
|
||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
const source = (fill.fillData as FillData).source;
|
if (fill.source === ERC20BridgeSource.Native) {
|
||||||
if (source === ERC20BridgeSource.Native) {
|
orders.push(createNativeOrder(fill));
|
||||||
orders.push((fill.fillData as NativeFillData).order);
|
|
||||||
} else {
|
} else {
|
||||||
orders.push(
|
orders.push(
|
||||||
createBridgeOrder(
|
createBridgeOrder(
|
||||||
orderDomain,
|
orderDomain,
|
||||||
this._getBridgeAddressFromSource(source),
|
fill,
|
||||||
|
this._getBridgeAddressFromSource(fill.source),
|
||||||
outputToken,
|
outputToken,
|
||||||
inputToken,
|
inputToken,
|
||||||
fill.output,
|
|
||||||
fill.input,
|
|
||||||
bridgeSlippage,
|
bridgeSlippage,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -54,23 +58,21 @@ export class CreateOrderUtils {
|
|||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
inputToken: string,
|
inputToken: string,
|
||||||
outputToken: string,
|
outputToken: string,
|
||||||
path: Fill[],
|
path: CollapsedFill[],
|
||||||
bridgeSlippage: number,
|
bridgeSlippage: number,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): OptimizedMarketOrder[] {
|
||||||
const orders: SignedOrderWithFillableAmounts[] = [];
|
const orders: OptimizedMarketOrder[] = [];
|
||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
const source = (fill.fillData as FillData).source;
|
if (fill.source === ERC20BridgeSource.Native) {
|
||||||
if (source === ERC20BridgeSource.Native) {
|
orders.push(createNativeOrder(fill));
|
||||||
orders.push((fill.fillData as NativeFillData).order);
|
|
||||||
} else {
|
} else {
|
||||||
orders.push(
|
orders.push(
|
||||||
createBridgeOrder(
|
createBridgeOrder(
|
||||||
orderDomain,
|
orderDomain,
|
||||||
this._getBridgeAddressFromSource(source),
|
fill,
|
||||||
|
this._getBridgeAddressFromSource(fill.source),
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
fill.input,
|
|
||||||
fill.output,
|
|
||||||
bridgeSlippage,
|
bridgeSlippage,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
@@ -97,14 +99,13 @@ export class CreateOrderUtils {
|
|||||||
|
|
||||||
function createBridgeOrder(
|
function createBridgeOrder(
|
||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
|
fill: CollapsedFill,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerAssetAmount: BigNumber,
|
|
||||||
takerAssetAmount: BigNumber,
|
|
||||||
slippage: number,
|
slippage: number,
|
||||||
isBuy: boolean = false,
|
isBuy: boolean = false,
|
||||||
): SignedOrderWithFillableAmounts {
|
): OptimizedMarketOrder {
|
||||||
return {
|
return {
|
||||||
makerAddress: bridgeAddress,
|
makerAddress: bridgeAddress,
|
||||||
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
|
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
@@ -113,7 +114,7 @@ function createBridgeOrder(
|
|||||||
createBridgeData(takerToken),
|
createBridgeData(takerToken),
|
||||||
),
|
),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(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<
|
type CommonOrderFields = Pick<
|
||||||
SignedOrderWithFillableAmounts,
|
OptimizedMarketOrder,
|
||||||
Exclude<keyof SignedOrderWithFillableAmounts, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
|
Exclude<keyof OptimizedMarketOrder, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
function createCommonOrderFields(
|
function createCommonOrderFields(
|
||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
makerAssetAmount: BigNumber,
|
fill: CollapsedFill,
|
||||||
takerAssetAmount: BigNumber,
|
|
||||||
slippage: number,
|
slippage: number,
|
||||||
isBuy: boolean = false,
|
isBuy: boolean = false,
|
||||||
): CommonOrderFields {
|
): CommonOrderFields {
|
||||||
const makerAssetAmountAdjustedWithSlippage = isBuy
|
const makerAssetAmountAdjustedWithSlippage = isBuy
|
||||||
? makerAssetAmount
|
? fill.totalMakerAssetAmount
|
||||||
: makerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
|
: fill.totalMakerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
|
||||||
const takerAssetAmountAdjustedWithSlippage = isBuy
|
const takerAssetAmountAdjustedWithSlippage = isBuy
|
||||||
? takerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
|
? fill.totalTakerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
|
||||||
: takerAssetAmount;
|
: fill.totalTakerAssetAmount;
|
||||||
return {
|
return {
|
||||||
|
fill,
|
||||||
takerAddress: NULL_ADDRESS,
|
takerAddress: NULL_ADDRESS,
|
||||||
senderAddress: NULL_ADDRESS,
|
senderAddress: NULL_ADDRESS,
|
||||||
feeRecipientAddress: NULL_ADDRESS,
|
feeRecipientAddress: NULL_ADDRESS,
|
||||||
@@ -159,3 +160,15 @@ function createCommonOrderFields(
|
|||||||
...orderDomain,
|
...orderDomain,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNativeOrder(fill: CollapsedFill): OptimizedMarketOrder {
|
||||||
|
return {
|
||||||
|
fill: {
|
||||||
|
source: fill.source,
|
||||||
|
totalMakerAssetAmount: fill.totalMakerAssetAmount,
|
||||||
|
totalTakerAssetAmount: fill.totalTakerAssetAmount,
|
||||||
|
subFills: fill.subFills,
|
||||||
|
},
|
||||||
|
...(fill as NativeCollapsedFill).nativeOrder,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@@ -14,12 +14,16 @@ import { comparePathOutputs, FillsOptimizer, getPathOutput } from './fill_optimi
|
|||||||
import { DexOrderSampler } from './sampler';
|
import { DexOrderSampler } from './sampler';
|
||||||
import {
|
import {
|
||||||
AggregationError,
|
AggregationError,
|
||||||
|
CollapsedFill,
|
||||||
DexSample,
|
DexSample,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
Fill,
|
Fill,
|
||||||
FillData,
|
FillData,
|
||||||
FillFlags,
|
FillFlags,
|
||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
|
NativeCollapsedFill,
|
||||||
|
NativeFillData,
|
||||||
|
OptimizedMarketOrder,
|
||||||
OrderDomain,
|
OrderDomain,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
@@ -53,7 +57,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
takerAmount: BigNumber,
|
takerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<SignedOrderWithFillableAmounts[]> {
|
): Promise<OptimizedMarketOrder[]> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@@ -77,7 +81,7 @@ export class MarketOperationUtils {
|
|||||||
takerAmount,
|
takerAmount,
|
||||||
_opts.dustFractionThreshold,
|
_opts.dustFractionThreshold,
|
||||||
);
|
);
|
||||||
const clippedNativePath = clipPathToInput(prunedNativePath, takerAmount);
|
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath), takerAmount);
|
||||||
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
|
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
|
||||||
const allPaths = [...dexPaths];
|
const allPaths = [...dexPaths];
|
||||||
const allFills = flattenDexPaths(dexPaths);
|
const allFills = flattenDexPaths(dexPaths);
|
||||||
@@ -105,7 +109,7 @@ export class MarketOperationUtils {
|
|||||||
this._orderDomain,
|
this._orderDomain,
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
simplifyPath(optimalPath),
|
collapsePath(optimalPath, false),
|
||||||
_opts.bridgeSlippage,
|
_opts.bridgeSlippage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -122,7 +126,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
makerAmount: BigNumber,
|
makerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<SignedOrderWithFillableAmounts[]> {
|
): Promise<OptimizedMarketOrder[]> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@@ -161,7 +165,7 @@ export class MarketOperationUtils {
|
|||||||
batchNativeOrders: SignedOrder[][],
|
batchNativeOrders: SignedOrder[][],
|
||||||
makerAmounts: BigNumber[],
|
makerAmounts: BigNumber[],
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<Array<SignedOrderWithFillableAmounts[] | undefined>> {
|
): Promise<Array<OptimizedMarketOrder[] | undefined>> {
|
||||||
if (batchNativeOrders.length === 0) {
|
if (batchNativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@@ -192,7 +196,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrderFillableAmounts: BigNumber[],
|
nativeOrderFillableAmounts: BigNumber[],
|
||||||
dexQuotes: DexSample[][],
|
dexQuotes: DexSample[][],
|
||||||
opts: GetMarketOrdersOpts,
|
opts: GetMarketOrdersOpts,
|
||||||
): SignedOrderWithFillableAmounts[] | undefined {
|
): OptimizedMarketOrder[] | undefined {
|
||||||
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
nativeOrderFillableAmounts,
|
nativeOrderFillableAmounts,
|
||||||
@@ -203,7 +207,7 @@ export class MarketOperationUtils {
|
|||||||
makerAmount,
|
makerAmount,
|
||||||
opts.dustFractionThreshold,
|
opts.dustFractionThreshold,
|
||||||
);
|
);
|
||||||
const clippedNativePath = clipPathToInput(prunedNativePath, makerAmount);
|
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath).reverse(), makerAmount);
|
||||||
const dexPaths = createPathsFromDexQuotes(dexQuotes, opts.noConflicts);
|
const dexPaths = createPathsFromDexQuotes(dexQuotes, opts.noConflicts);
|
||||||
const allPaths = [...dexPaths];
|
const allPaths = [...dexPaths];
|
||||||
const allFills = flattenDexPaths(dexPaths);
|
const allFills = flattenDexPaths(dexPaths);
|
||||||
@@ -230,7 +234,7 @@ export class MarketOperationUtils {
|
|||||||
this._orderDomain,
|
this._orderDomain,
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
simplifyPath(optimalPath),
|
collapsePath(optimalPath, true),
|
||||||
opts.bridgeSlippage,
|
opts.bridgeSlippage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -242,8 +246,7 @@ function createSignedOrdersWithFillableAmounts(
|
|||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): SignedOrderWithFillableAmounts[] {
|
||||||
return signedOrders
|
return signedOrders
|
||||||
.map(
|
.map((order: SignedOrder, i: number) => {
|
||||||
(order: SignedOrder, i: number): SignedOrderWithFillableAmounts => {
|
|
||||||
const fillableAmount = fillableAmounts[i];
|
const fillableAmount = fillableAmounts[i];
|
||||||
const fillableMakerAssetAmount =
|
const fillableMakerAssetAmount =
|
||||||
operation === MarketOperation.Buy
|
operation === MarketOperation.Buy
|
||||||
@@ -260,8 +263,7 @@ function createSignedOrdersWithFillableAmounts(
|
|||||||
fillableTakerFeeAmount,
|
fillableTakerFeeAmount,
|
||||||
...order,
|
...order,
|
||||||
};
|
};
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.filter(order => {
|
.filter(order => {
|
||||||
return !order.fillableMakerAssetAmount.isZero() && !order.fillableTakerAssetAmount.isZero();
|
return !order.fillableMakerAssetAmount.isZero() && !order.fillableTakerAssetAmount.isZero();
|
||||||
});
|
});
|
||||||
@@ -412,23 +414,31 @@ function getPathInput(path: Fill[]): BigNumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merges contiguous fills from the same DEX.
|
// Merges contiguous fills from the same DEX.
|
||||||
function simplifyPath(path: Fill[]): Fill[] {
|
function collapsePath(path: Fill[], isBuy: boolean): CollapsedFill[] {
|
||||||
const simplified: Fill[] = [];
|
const collapsed: Array<CollapsedFill | NativeCollapsedFill> = [];
|
||||||
for (const fill of path) {
|
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;
|
const source = (fill.fillData as FillData).source;
|
||||||
if (simplified.length !== 0 && source !== ERC20BridgeSource.Native) {
|
if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) {
|
||||||
const prevFill = simplified[simplified.length - 1];
|
const prevFill = collapsed[collapsed.length - 1];
|
||||||
const prevSource = (prevFill.fillData as FillData).source;
|
|
||||||
// If the last fill is from the same source, merge them.
|
// If the last fill is from the same source, merge them.
|
||||||
if (prevSource === source) {
|
if (prevFill.source === source) {
|
||||||
prevFill.input = prevFill.input.plus(fill.input);
|
prevFill.totalMakerAssetAmount = prevFill.totalMakerAssetAmount.plus(makerAssetAmount);
|
||||||
prevFill.output = prevFill.output.plus(fill.output);
|
prevFill.totalTakerAssetAmount = prevFill.totalTakerAssetAmount.plus(takerAssetAmount);
|
||||||
|
prevFill.subFills.push({ makerAssetAmount, takerAssetAmount });
|
||||||
continue;
|
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.
|
// Sort fills by descending price.
|
||||||
|
@@ -79,6 +79,48 @@ export interface Fill {
|
|||||||
fillData: FillData | NativeFillData;
|
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()`.
|
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
|
||||||
*/
|
*/
|
||||||
|
@@ -16,6 +16,7 @@ import {
|
|||||||
|
|
||||||
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
||||||
import { MarketOperationUtils } from './market_operation_utils';
|
import { MarketOperationUtils } from './market_operation_utils';
|
||||||
|
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
|
||||||
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
||||||
import { utils } from './utils';
|
import { utils } from './utils';
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ export class SwapQuoteCalculator {
|
|||||||
opts,
|
opts,
|
||||||
)) as Array<MarketBuySwapQuote | undefined>;
|
)) as Array<MarketBuySwapQuote | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calculateBatchBuySwapQuoteAsync(
|
private async _calculateBatchBuySwapQuoteAsync(
|
||||||
batchPrunedOrders: SignedOrder[][],
|
batchPrunedOrders: SignedOrder[][],
|
||||||
assetFillAmounts: BigNumber[],
|
assetFillAmounts: BigNumber[],
|
||||||
@@ -106,7 +108,6 @@ export class SwapQuoteCalculator {
|
|||||||
operation,
|
operation,
|
||||||
assetFillAmounts[i],
|
assetFillAmounts[i],
|
||||||
gasPrice,
|
gasPrice,
|
||||||
opts,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
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
|
// 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();
|
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||||
let resultOrders: SignedOrderWithFillableAmounts[] = [];
|
let resultOrders: OptimizedMarketOrder[] = [];
|
||||||
|
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
@@ -151,37 +152,35 @@ export class SwapQuoteCalculator {
|
|||||||
operation,
|
operation,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
opts,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private async _createSwapQuoteAsync(
|
private async _createSwapQuoteAsync(
|
||||||
makerAssetData: string,
|
makerAssetData: string,
|
||||||
takerAssetData: string,
|
takerAssetData: string,
|
||||||
resultOrders: SignedOrderWithFillableAmounts[],
|
resultOrders: OptimizedMarketOrder[],
|
||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
opts: CalculateSwapQuoteOpts,
|
|
||||||
): Promise<SwapQuote> {
|
): Promise<SwapQuote> {
|
||||||
const bestCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
const bestCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
||||||
createBestCaseOrders(resultOrders, operation, opts.bridgeSlippage),
|
resultOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
operation,
|
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(
|
const worstCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
||||||
_.reverse(resultOrders.slice()),
|
resultOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
operation,
|
operation,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
const quoteBase = {
|
const quoteBase = {
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
orders: resultOrders,
|
// Remove fill metadata.
|
||||||
|
orders: resultOrders.map(o => _.omit(o, 'fill')) as SignedOrderWithFillableAmounts[],
|
||||||
bestCaseQuoteInfo,
|
bestCaseQuoteInfo,
|
||||||
worstCaseQuoteInfo,
|
worstCaseQuoteInfo,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
@@ -204,31 +203,39 @@ export class SwapQuoteCalculator {
|
|||||||
|
|
||||||
// tslint:disable-next-line: prefer-function-over-method
|
// tslint:disable-next-line: prefer-function-over-method
|
||||||
private async _calculateQuoteInfoAsync(
|
private async _calculateQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
return this._calculateMarketBuyQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
|
return this._calculateMarketBuyQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
|
||||||
} else {
|
} else {
|
||||||
return this._calculateMarketSellQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
|
return this._calculateMarketSellQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calculateMarketSellQuoteInfoAsync(
|
private async _calculateMarketSellQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
takerAssetSellAmount: BigNumber,
|
takerAssetSellAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
const result = prunedOrders.reduce(
|
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
(acc, order) => {
|
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const {
|
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
totalMakerAssetAmount,
|
let remainingTakerAssetFillAmount = takerAssetSellAmount;
|
||||||
totalTakerAssetAmount,
|
const filledOrders = [] as OptimizedMarketOrder[];
|
||||||
totalFeeTakerAssetAmount,
|
const _orders = !worstCase ? orders : orders.slice().reverse();
|
||||||
remainingTakerAssetFillAmount,
|
for (const order of _orders) {
|
||||||
} = acc;
|
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(
|
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
@@ -239,99 +246,155 @@ export class SwapQuoteCalculator {
|
|||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
adjustedFillableTakerAssetAmount,
|
adjustedFillableTakerAssetAmount,
|
||||||
);
|
);
|
||||||
const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown(
|
const takerAssetAmountBreakDown = getTakerAssetAmountBreakDown(order, takerAssetAmountWithFees);
|
||||||
order,
|
takerAssetAmount = takerAssetAmountBreakDown.takerAssetAmount;
|
||||||
takerAssetAmountWithFees,
|
feeTakerAssetAmount = takerAssetAmountBreakDown.feeTakerAssetAmount;
|
||||||
);
|
makerAssetAmount = takerAssetAmountWithFees
|
||||||
const makerAssetAmount = takerAssetAmountWithFees
|
|
||||||
.div(adjustedFillableTakerAssetAmount)
|
.div(adjustedFillableTakerAssetAmount)
|
||||||
.multipliedBy(adjustedFillableMakerAssetAmount)
|
.times(adjustedFillableMakerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
return {
|
} else {
|
||||||
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerAssetAmount),
|
// This is a collapsed bridge order.
|
||||||
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
|
// Because collapsed bridge orders actually fill at different rates,
|
||||||
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
|
// we can iterate over the uncollapsed fills to get the actual
|
||||||
remainingTakerAssetFillAmount: BigNumber.max(
|
// asset amounts transfered.
|
||||||
constants.ZERO_AMOUNT,
|
// We can also assume there are no fees and the order is not
|
||||||
remainingTakerAssetFillAmount.minus(takerAssetAmountWithFees),
|
// partially filled.
|
||||||
),
|
|
||||||
};
|
// Infer the bridge slippage from the difference between the fill
|
||||||
},
|
// size and the atual order asset amounts.
|
||||||
{
|
const makerAssetBridgeSlippage = !worstCase
|
||||||
totalMakerAssetAmount: constants.ZERO_AMOUNT,
|
? constants.ONE_AMOUNT
|
||||||
totalTakerAssetAmount: constants.ZERO_AMOUNT,
|
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
|
||||||
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
|
const takerAssetBridgeSlippage = !worstCase
|
||||||
remainingTakerAssetFillAmount: takerAssetSellAmount,
|
? 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(
|
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
||||||
prunedOrders,
|
filledOrders,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
|
feeTakerAssetAmount: totalFeeTakerAssetAmount,
|
||||||
takerAssetAmount: result.totalTakerAssetAmount,
|
takerAssetAmount: totalTakerAssetAmount,
|
||||||
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
|
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
|
||||||
makerAssetAmount: result.totalMakerAssetAmount,
|
makerAssetAmount: totalMakerAssetAmount,
|
||||||
protocolFeeInWeiAmount,
|
protocolFeeInWeiAmount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calculateMarketBuyQuoteInfoAsync(
|
private async _calculateMarketBuyQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetBuyAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
const result = prunedOrders.reduce(
|
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
(acc, order) => {
|
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const {
|
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
totalMakerAssetAmount,
|
let remainingMakerAssetFillAmount = makerAssetBuyAmount;
|
||||||
totalTakerAssetAmount,
|
const filledOrders = [] as OptimizedMarketOrder[];
|
||||||
totalFeeTakerAssetAmount,
|
const _orders = !worstCase ? orders : orders.slice().reverse();
|
||||||
remainingMakerAssetFillAmount,
|
for (const order of _orders) {
|
||||||
} = acc;
|
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(
|
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
const adjustedFillableTakerAssetAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterFees(
|
const adjustedFillableTakerAssetAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
const makerFillAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
|
makerAssetAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
|
||||||
const takerAssetAmountWithFees = makerFillAmount
|
const takerAssetAmountWithFees = makerAssetAmount
|
||||||
.div(adjustedFillableMakerAssetAmount)
|
.div(adjustedFillableMakerAssetAmount)
|
||||||
.multipliedBy(adjustedFillableTakerAssetAmount)
|
.multipliedBy(adjustedFillableTakerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_UP);
|
.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(
|
// Infer the bridge slippage from the difference between the fill
|
||||||
order,
|
// size and the atual order asset amounts.
|
||||||
takerAssetAmountWithFees,
|
const makerAssetBridgeSlippage = !worstCase
|
||||||
);
|
? constants.ONE_AMOUNT
|
||||||
return {
|
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
|
||||||
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerFillAmount),
|
const takerAssetBridgeSlippage = !worstCase
|
||||||
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
|
? constants.ONE_AMOUNT
|
||||||
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
|
: order.takerAssetAmount.div(order.fill.totalTakerAssetAmount);
|
||||||
remainingMakerAssetFillAmount: BigNumber.max(
|
// Consecutively fill the subfills in this order.
|
||||||
constants.ZERO_AMOUNT,
|
const subFills = !worstCase ? order.fill.subFills : order.fill.subFills.slice().reverse();
|
||||||
remainingMakerAssetFillAmount.minus(makerFillAmount),
|
for (const subFill of subFills) {
|
||||||
),
|
if (remainingMakerAssetFillAmount.minus(makerAssetAmount).lte(0)) {
|
||||||
};
|
break;
|
||||||
},
|
}
|
||||||
{
|
const partialTakerAssetAmount = subFill.takerAssetAmount.times(takerAssetBridgeSlippage);
|
||||||
totalMakerAssetAmount: constants.ZERO_AMOUNT,
|
const partialMakerAssetAmount = subFill.makerAssetAmount.times(makerAssetBridgeSlippage);
|
||||||
totalTakerAssetAmount: constants.ZERO_AMOUNT,
|
const partialMakerAssetFillAmount = BigNumber.min(
|
||||||
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
|
partialMakerAssetAmount,
|
||||||
remainingMakerAssetFillAmount: makerAssetBuyAmount,
|
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(
|
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
||||||
prunedOrders,
|
filledOrders,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
|
feeTakerAssetAmount: totalFeeTakerAssetAmount,
|
||||||
takerAssetAmount: result.totalTakerAssetAmount,
|
takerAssetAmount: totalTakerAssetAmount,
|
||||||
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
|
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
|
||||||
makerAssetAmount: result.totalMakerAssetAmount,
|
makerAssetAmount: totalMakerAssetAmount,
|
||||||
protocolFeeInWeiAmount,
|
protocolFeeInWeiAmount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -372,35 +435,3 @@ function getTakerAssetAmountBreakDown(
|
|||||||
takerAssetAmount: takerAssetAmountWithFees,
|
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;
|
|
||||||
}
|
|
||||||
|
@@ -23,7 +23,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9));
|
||||||
});
|
});
|
||||||
it('should provide correct liquidity result with orders with takerFees in takerAsset', () => {
|
it('should provide correct liquidity result with orders with takerFees in takerAsset', () => {
|
||||||
@@ -31,7 +31,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15));
|
||||||
});
|
});
|
||||||
it('should provide correct liquidity result with orders with takerFees in makerAsset', () => {
|
it('should provide correct liquidity result with orders with takerFees in makerAsset', () => {
|
||||||
@@ -51,7 +51,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(25));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(27));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -65,6 +65,9 @@ const createSamplerFromSignedOrdersWithFillableAmounts = (
|
|||||||
return sampler;
|
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:max-file-line-count
|
||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
describe('swapQuoteCalculator', () => {
|
describe('swapQuoteCalculator', () => {
|
||||||
@@ -272,7 +275,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with slippage (feeless orders)', async () => {
|
it('calculates a correct swapQuote with slippage (feeless orders)', async () => {
|
||||||
const assetSellAmount = baseUnitAmount(1);
|
const assetSellAmount = baseUnitAmount(4);
|
||||||
const slippagePercentage = 0.2;
|
const slippagePercentage = 0.2;
|
||||||
const sampler = createSamplerFromSignedOrdersWithFillableAmounts(
|
const sampler = createSamplerFromSignedOrdersWithFillableAmounts(
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS,
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS,
|
||||||
@@ -289,10 +292,10 @@ describe('swapQuoteCalculator', () => {
|
|||||||
GAS_PRICE,
|
GAS_PRICE,
|
||||||
CALCULATE_SWAP_QUOTE_OPTS,
|
CALCULATE_SWAP_QUOTE_OPTS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
||||||
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[2],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
@@ -301,15 +304,15 @@ describe('swapQuoteCalculator', () => {
|
|||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: assetSellAmount,
|
takerAssetAmount: assetSellAmount,
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(9),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: assetSellAmount,
|
takerAssetAmount: assetSellAmount,
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.4),
|
makerAssetAmount: baseUnitAmount(1.6),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', async () => {
|
||||||
@@ -372,7 +375,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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[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);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
@@ -381,14 +384,14 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(4.5),
|
makerAssetAmount: baseUnitAmount(4.5),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0.5),
|
feeTakerAssetAmount: baseUnitAmount(1.2),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(0.5)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.2)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(1),
|
makerAssetAmount: baseUnitAmount(1.8),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', async () => {
|
||||||
@@ -411,23 +414,24 @@ describe('swapQuoteCalculator', () => {
|
|||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(4),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(4),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', async () => {
|
||||||
@@ -451,24 +455,24 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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[1],
|
||||||
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// 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({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(2),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(0.8),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 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),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -678,7 +682,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
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);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
|
|
||||||
@@ -692,12 +696,16 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount,
|
takerAssetAmount,
|
||||||
totalTakerAssetAmount: takerAssetAmount,
|
totalTakerAssetAmount: takerAssetAmount,
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: baseUnitAmount(5.5),
|
takerAssetAmount: baseUnitAmount(20)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5.5),
|
.div(6)
|
||||||
|
.integerValue(BigNumber.ROUND_UP),
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(20)
|
||||||
|
.div(6)
|
||||||
|
.integerValue(BigNumber.ROUND_UP),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@@ -766,7 +774,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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[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);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
@@ -775,12 +783,16 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount: fiveSixthEthInWei,
|
takerAssetAmount: fiveSixthEthInWei,
|
||||||
totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei),
|
totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(3),
|
||||||
takerAssetAmount: baseUnitAmount(5.5),
|
takerAssetAmount: baseUnitAmount(10)
|
||||||
totalTakerAssetAmount: baseUnitAmount(8),
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 3.3333...
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(19)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 6.3333...
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@@ -805,21 +817,33 @@ describe('swapQuoteCalculator', () => {
|
|||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(0.5)
|
||||||
takerAssetAmount: baseUnitAmount(2.5),
|
.div(3)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5),
|
.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,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(0.5)
|
||||||
takerAssetAmount: baseUnitAmount(2.5),
|
.div(3)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5),
|
.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,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
@@ -845,14 +869,14 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
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[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);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.75),
|
feeTakerAssetAmount: baseUnitAmount(1.25).minus(1),
|
||||||
takerAssetAmount: baseUnitAmount(2.75),
|
takerAssetAmount: baseUnitAmount(2.25).plus(1),
|
||||||
totalTakerAssetAmount: baseUnitAmount(5.5),
|
totalTakerAssetAmount: baseUnitAmount(3.5),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@@ -879,7 +903,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
.multipliedBy(0.1)
|
.multipliedBy(0.1)
|
||||||
.integerValue(BigNumber.ROUND_CEIL),
|
.integerValue(BigNumber.ROUND_CEIL),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -51,7 +51,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS: Array<Partial<SignedOrderWit
|
|||||||
takerAssetAmount: baseUnitAmount(6),
|
takerAssetAmount: baseUnitAmount(6),
|
||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(6),
|
||||||
fillableTakerAssetAmount: baseUnitAmount(3),
|
fillableTakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableMakerAssetAmount: baseUnitAmount(2),
|
fillableMakerAssetAmount: baseUnitAmount(3),
|
||||||
},
|
},
|
||||||
...PARTIAL_ORDER,
|
...PARTIAL_ORDER,
|
||||||
},
|
},
|
||||||
@@ -86,7 +86,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET: Array<Partial<Sig
|
|||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(6),
|
||||||
takerFee: baseUnitAmount(4),
|
takerFee: baseUnitAmount(4),
|
||||||
fillableTakerAssetAmount: baseUnitAmount(3),
|
fillableTakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableMakerAssetAmount: baseUnitAmount(2),
|
fillableMakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableTakerFeeAmount: baseUnitAmount(2),
|
fillableTakerFeeAmount: baseUnitAmount(2),
|
||||||
},
|
},
|
||||||
...PARTIAL_ORDER_FEE_IN_TAKER_ASSET,
|
...PARTIAL_ORDER_FEE_IN_TAKER_ASSET,
|
||||||
|
Reference in New Issue
Block a user