rewritten test cases for SwapQuoter
fixed testing for swap quoter + setup for consumer testing added framework for testing consumers added testing and updated some types
This commit is contained in:
parent
222f7e6fd4
commit
e1ab9aa690
@ -17,7 +17,7 @@
|
|||||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||||
"test:circleci": "yarn test:coverage",
|
"test:circleci": "yarn test:coverage",
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit",
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 10000 --bail --exit",
|
||||||
"clean": "shx rm -rf lib test_temp",
|
"clean": "shx rm -rf lib test_temp",
|
||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
@ -39,8 +39,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/assert": "^2.0.10",
|
"@0x/assert": "^2.0.10",
|
||||||
"@0x/connect": "^5.0.10",
|
"@0x/connect": "^5.0.10",
|
||||||
|
"@0x/contract-addresses": "^2.3.3",
|
||||||
"@0x/contract-wrappers": "^9.1.4",
|
"@0x/contract-wrappers": "^9.1.4",
|
||||||
|
"@0x/dev-utils": "^2.2.3",
|
||||||
|
"@0x/fill-scenarios": "^3.0.10",
|
||||||
"@0x/json-schemas": "^3.0.10",
|
"@0x/json-schemas": "^3.0.10",
|
||||||
|
"@0x/migrations": "^4.1.6",
|
||||||
"@0x/order-utils": "^8.1.1",
|
"@0x/order-utils": "^8.1.1",
|
||||||
"@0x/subproviders": "^4.1.0",
|
"@0x/subproviders": "^4.1.0",
|
||||||
"@0x/types": "^2.2.2",
|
"@0x/types": "^2.2.2",
|
||||||
|
@ -22,7 +22,6 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
|||||||
const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = {
|
const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = {
|
||||||
feePercentage: 0,
|
feePercentage: 0,
|
||||||
feeRecipient: NULL_ADDRESS,
|
feeRecipient: NULL_ADDRESS,
|
||||||
ethAmount: new BigNumber(0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
||||||
|
@ -18,7 +18,8 @@ export {
|
|||||||
export { SignedOrder } from '@0x/types';
|
export { SignedOrder } from '@0x/types';
|
||||||
export { BigNumber } from '@0x/utils';
|
export { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
export { SwapQuoter } from './asset_buyer';
|
export { ForwarderSwapQuoteConsumer } from './quote_consumers/forwarder_swap_quote_consumer';
|
||||||
|
export { SwapQuoter } from './swap_quoter';
|
||||||
export { InsufficientAssetLiquidityError } from './errors';
|
export { InsufficientAssetLiquidityError } from './errors';
|
||||||
|
|
||||||
export { BasicOrderProvider } from './order_providers/basic_order_provider';
|
export { BasicOrderProvider } from './order_providers/basic_order_provider';
|
||||||
|
@ -22,21 +22,13 @@ import { assetDataUtils } from '../utils/asset_data_utils';
|
|||||||
import { utils } from '../utils/utils';
|
import { utils } from '../utils/utils';
|
||||||
|
|
||||||
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMarketBuySmartContractParams> {
|
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMarketBuySmartContractParams> {
|
||||||
|
|
||||||
public readonly provider: ZeroExProvider;
|
public readonly provider: ZeroExProvider;
|
||||||
public readonly networkId: number;
|
public readonly networkId: number;
|
||||||
|
|
||||||
private readonly _contractWrappers: ContractWrappers;
|
private readonly _contractWrappers: ContractWrappers;
|
||||||
|
|
||||||
constructor(
|
constructor(supportedProvider: SupportedProvider, options: Partial<SwapQuoteConsumerOpts> = {}) {
|
||||||
supportedProvider: SupportedProvider,
|
const { networkId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
||||||
options: Partial<SwapQuoteConsumerOpts> = {},
|
|
||||||
) {
|
|
||||||
const { networkId } = _.merge(
|
|
||||||
{},
|
|
||||||
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
assert.isNumber('networkId', networkId);
|
assert.isNumber('networkId', networkId);
|
||||||
|
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
@ -64,12 +56,16 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
|||||||
const calldataHexString = abiEncoder.encode(args);
|
const calldataHexString = abiEncoder.encode(args);
|
||||||
return {
|
return {
|
||||||
calldataHexString,
|
calldataHexString,
|
||||||
|
methodAbi,
|
||||||
to,
|
to,
|
||||||
ethAmount,
|
ethAmount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSmartContractParamsOrThrow(quote: SwapQuote, opts: Partial<ForwarderSwapQuoteGetOutputOpts>): SmartContractParamsInfo<ForwarderMarketBuySmartContractParams> {
|
public getSmartContractParamsOrThrow(
|
||||||
|
quote: SwapQuote,
|
||||||
|
opts: Partial<ForwarderSwapQuoteGetOutputOpts>,
|
||||||
|
): SmartContractParamsInfo<ForwarderMarketBuySmartContractParams> {
|
||||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||||
|
|
||||||
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
||||||
@ -80,14 +76,19 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
|||||||
|
|
||||||
assert.isNumber('feePercentage', unFormattedFeePercentage);
|
assert.isNumber('feePercentage', unFormattedFeePercentage);
|
||||||
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
||||||
assert.isBigNumber('ethAmount', ethAmount);
|
if (ethAmount !== undefined) {
|
||||||
|
assert.isBigNumber('ethAmount', ethAmount);
|
||||||
|
}
|
||||||
|
|
||||||
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, unFormattedFeePercentage);
|
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(
|
||||||
|
quote,
|
||||||
|
unFormattedFeePercentage,
|
||||||
|
);
|
||||||
|
|
||||||
const { orders, feeOrders, makerAssetFillAmount, worstCaseQuoteInfo } = swapQuoteWithAffiliateFee;
|
const { orders, feeOrders, makerAssetFillAmount, worstCaseQuoteInfo } = swapQuoteWithAffiliateFee;
|
||||||
|
|
||||||
const signatures = _.map(orders, o => o.signature);
|
const signatures = _.map(orders, o => o.signature);
|
||||||
const feeSignatures = _.map(orders, o => o.signature);
|
const feeSignatures = _.map(feeOrders, o => o.signature);
|
||||||
|
|
||||||
const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage);
|
const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage);
|
||||||
|
|
||||||
@ -101,7 +102,11 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
|||||||
feeRecipient,
|
feeRecipient,
|
||||||
};
|
};
|
||||||
|
|
||||||
const methodAbi = utils.getMethodAbiFromContractAbi(this._contractWrappers.forwarder.abi, 'marketBuyOrdersWithEth') as MethodAbi;
|
const methodAbi = utils.getMethodAbiFromContractAbi(
|
||||||
|
this._contractWrappers.forwarder.abi,
|
||||||
|
'marketBuyOrdersWithEth',
|
||||||
|
) as MethodAbi;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
params,
|
params,
|
||||||
to: this._contractWrappers.forwarder.address,
|
to: this._contractWrappers.forwarder.address,
|
||||||
@ -110,7 +115,10 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async executeSwapQuoteOrThrowAsync(quote: SwapQuote, opts: Partial<ForwarderSwapQuoteExecutionOpts>): Promise<string> {
|
public async executeSwapQuoteOrThrowAsync(
|
||||||
|
quote: SwapQuote,
|
||||||
|
opts: Partial<ForwarderSwapQuoteExecutionOpts>,
|
||||||
|
): Promise<string> {
|
||||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||||
|
|
||||||
const { ethAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge(
|
const { ethAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge(
|
||||||
@ -121,8 +129,9 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
|||||||
|
|
||||||
assert.isNumber('feePercentage', feePercentage);
|
assert.isNumber('feePercentage', feePercentage);
|
||||||
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
||||||
assert.isBigNumber('ethAmount', ethAmount);
|
if (ethAmount !== undefined) {
|
||||||
|
assert.isBigNumber('ethAmount', ethAmount);
|
||||||
|
}
|
||||||
if (takerAddress !== undefined) {
|
if (takerAddress !== undefined) {
|
||||||
assert.isETHAddressHex('takerAddress', takerAddress);
|
assert.isETHAddressHex('takerAddress', takerAddress);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,11 @@ export class SwapQuoter {
|
|||||||
*
|
*
|
||||||
* @return An instance of SwapQuoter
|
* @return An instance of SwapQuoter
|
||||||
*/
|
*/
|
||||||
constructor(supportedProvider: SupportedProvider, orderProvider: OrderProvider, options: Partial<SwapQuoterOpts> = {}) {
|
constructor(
|
||||||
|
supportedProvider: SupportedProvider,
|
||||||
|
orderProvider: OrderProvider,
|
||||||
|
options: Partial<SwapQuoterOpts> = {},
|
||||||
|
) {
|
||||||
const { networkId, orderRefreshIntervalMs, expiryBufferMs } = _.merge(
|
const { networkId, orderRefreshIntervalMs, expiryBufferMs } = _.merge(
|
||||||
{},
|
{},
|
||||||
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
||||||
@ -202,7 +206,7 @@ export class SwapQuoter {
|
|||||||
): Promise<LiquidityForAssetData> {
|
): Promise<LiquidityForAssetData> {
|
||||||
const shouldForceOrderRefresh =
|
const shouldForceOrderRefresh =
|
||||||
options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
|
options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
|
||||||
assert.isString('makerAssetDataa', makerAssetData);
|
assert.isString('makerAssetData', makerAssetData);
|
||||||
assert.isString('takerAssetData', takerAssetData);
|
assert.isString('takerAssetData', takerAssetData);
|
||||||
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
|
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
|
||||||
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
|
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
|
@ -47,6 +47,7 @@ export interface OrderProvider {
|
|||||||
*/
|
*/
|
||||||
export interface CalldataInfo {
|
export interface CalldataInfo {
|
||||||
calldataHexString: string;
|
calldataHexString: string;
|
||||||
|
methodAbi: MethodAbi;
|
||||||
to: string;
|
to: string;
|
||||||
ethAmount?: BigNumber;
|
ethAmount?: BigNumber;
|
||||||
}
|
}
|
||||||
@ -128,14 +129,13 @@ export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts {
|
|||||||
export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts {
|
export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts {
|
||||||
feePercentage: number;
|
feePercentage: number;
|
||||||
feeRecipient: string;
|
feeRecipient: string;
|
||||||
ethAmount: BigNumber;
|
ethAmount?: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the options for executing a swap quote with ForwarderSwapQuoteConusmer
|
* Represents the options for executing a swap quote with ForwarderSwapQuoteConusmer
|
||||||
*/
|
*/
|
||||||
export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {
|
export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
* takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
||||||
|
@ -11,7 +11,7 @@ export const swapQuoteCalculator = {
|
|||||||
calculate(
|
calculate(
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetFillAmount: BigNumber,
|
||||||
slippagePercentage: number,
|
slippagePercentage: number,
|
||||||
isMakerAssetZrxToken: boolean,
|
isMakerAssetZrxToken: boolean,
|
||||||
): SwapQuote {
|
): SwapQuote {
|
||||||
@ -19,20 +19,20 @@ export const swapQuoteCalculator = {
|
|||||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||||
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||||
const slippageBufferAmount = makerAssetBuyAmount.multipliedBy(slippagePercentage).integerValue();
|
const slippageBufferAmount = makerAssetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||||
// find the orders that cover the desired assetBuyAmount (with slippage)
|
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||||
const {
|
const {
|
||||||
resultOrders,
|
resultOrders,
|
||||||
remainingFillAmount,
|
remainingFillAmount,
|
||||||
ordersRemainingFillableMakerAssetAmounts,
|
ordersRemainingFillableMakerAssetAmounts,
|
||||||
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetBuyAmount, {
|
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetFillAmount, {
|
||||||
remainingFillableMakerAssetAmounts,
|
remainingFillableMakerAssetAmounts,
|
||||||
slippageBufferAmount,
|
slippageBufferAmount,
|
||||||
});
|
});
|
||||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||||
// We needed the amount they requested to buy, plus the amount for slippage
|
// We needed the amount they requested to buy, plus the amount for slippage
|
||||||
const totalAmountRequested = makerAssetBuyAmount.plus(slippageBufferAmount);
|
const totalAmountRequested = makerAssetFillAmount.plus(slippageBufferAmount);
|
||||||
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
||||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||||
// in order to get the total amount needed considering slippage
|
// in order to get the total amount needed considering slippage
|
||||||
@ -87,21 +87,21 @@ export const swapQuoteCalculator = {
|
|||||||
const bestCaseQuoteInfo = calculateQuoteInfo(
|
const bestCaseQuoteInfo = calculateQuoteInfo(
|
||||||
trimmedOrdersAndFillableAmounts,
|
trimmedOrdersAndFillableAmounts,
|
||||||
trimmedFeeOrdersAndFillableAmounts,
|
trimmedFeeOrdersAndFillableAmounts,
|
||||||
makerAssetBuyAmount,
|
makerAssetFillAmount,
|
||||||
isMakerAssetZrxToken,
|
isMakerAssetZrxToken,
|
||||||
);
|
);
|
||||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||||
const worstCaseQuoteInfo = calculateQuoteInfo(
|
const worstCaseQuoteInfo = calculateQuoteInfo(
|
||||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||||
makerAssetBuyAmount,
|
makerAssetFillAmount,
|
||||||
isMakerAssetZrxToken,
|
isMakerAssetZrxToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
makerAssetBuyAmount,
|
makerAssetFillAmount,
|
||||||
orders: resultOrders,
|
orders: resultOrders,
|
||||||
feeOrders: resultFeeOrders,
|
feeOrders: resultFeeOrders,
|
||||||
bestCaseQuoteInfo,
|
bestCaseQuoteInfo,
|
||||||
@ -191,30 +191,30 @@ function findTakerTokenAmountNeededToBuyZrx(
|
|||||||
|
|
||||||
function findTakerTokenAndZrxAmountNeededToBuyAsset(
|
function findTakerTokenAndZrxAmountNeededToBuyAsset(
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetFillAmount: BigNumber,
|
||||||
): [BigNumber, BigNumber] {
|
): [BigNumber, BigNumber] {
|
||||||
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
|
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
|
||||||
const result = _.reduce(
|
const result = _.reduce(
|
||||||
orders,
|
orders,
|
||||||
(acc, order, index) => {
|
(acc, order, index) => {
|
||||||
const { totalTakerTokenAmount, totalZrxAmount, remainingMakerAssetBuyAmount } = acc;
|
const { totalTakerTokenAmount, totalZrxAmount, remainingmakerAssetFillAmount } = acc;
|
||||||
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
|
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
|
||||||
const makerFillAmount = BigNumber.min(acc.remainingMakerAssetBuyAmount, remainingFillableMakerAssetAmount);
|
const makerFillAmount = BigNumber.min(acc.remainingmakerAssetFillAmount, remainingFillableMakerAssetAmount);
|
||||||
const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount);
|
const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount);
|
||||||
const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount);
|
const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount);
|
||||||
return {
|
return {
|
||||||
totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount),
|
totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount),
|
||||||
totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
|
totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
|
||||||
remainingMakerAssetBuyAmount: BigNumber.max(
|
remainingmakerAssetFillAmount: BigNumber.max(
|
||||||
constants.ZERO_AMOUNT,
|
constants.ZERO_AMOUNT,
|
||||||
remainingMakerAssetBuyAmount.minus(makerFillAmount),
|
remainingmakerAssetFillAmount.minus(makerFillAmount),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
totalTakerTokenAmount: constants.ZERO_AMOUNT,
|
totalTakerTokenAmount: constants.ZERO_AMOUNT,
|
||||||
totalZrxAmount: constants.ZERO_AMOUNT,
|
totalZrxAmount: constants.ZERO_AMOUNT,
|
||||||
remainingMakerAssetBuyAmount: makerAssetBuyAmount,
|
remainingmakerAssetFillAmount: makerAssetFillAmount,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return [result.totalTakerTokenAmount, result.totalZrxAmount];
|
return [result.totalTakerTokenAmount, result.totalZrxAmount];
|
||||||
|
@ -15,7 +15,7 @@ export const utils = {
|
|||||||
return _.find(
|
return _.find(
|
||||||
abi,
|
abi,
|
||||||
(def: AbiDefinition): boolean => {
|
(def: AbiDefinition): boolean => {
|
||||||
if (def.type === `'function'`) {
|
if (def.type === 'function') {
|
||||||
const methodDef = def as MethodAbi;
|
const methodDef = def as MethodAbi;
|
||||||
return methodDef.name === name;
|
return methodDef.name === name;
|
||||||
} else {
|
} else {
|
||||||
|
142
packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts
Normal file
142
packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils';
|
||||||
|
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
|
||||||
|
import { FillScenarios } from '@0x/fill-scenarios';
|
||||||
|
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||||
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import 'mocha';
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
|
||||||
|
import { ForwarderSwapQuoteConsumer, SwapQuote } from '../src';
|
||||||
|
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
import { migrateOnceAsync } from './utils/migrate';
|
||||||
|
import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFees } from './utils/swap_quote';
|
||||||
|
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
const FILLABLE_AMOUNTS = [new BigNumber(5), new BigNumber(10)];
|
||||||
|
const TESTRPC_NETWORK_ID = 50;
|
||||||
|
|
||||||
|
describe('ForwarderSwapQuoteConsumer', () => {
|
||||||
|
let userAddresses: string[];
|
||||||
|
let makerAddress: string;
|
||||||
|
let takerAddress: string;
|
||||||
|
let fillScenarios: FillScenarios;
|
||||||
|
let feeRecipient: string;
|
||||||
|
let makerAssetData: string;
|
||||||
|
let takerAssetData: string;
|
||||||
|
let wethAssetData: string;
|
||||||
|
const networkId = TESTRPC_NETWORK_ID;
|
||||||
|
before(async () => {
|
||||||
|
const contractAddresses = await migrateOnceAsync();
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
fillScenarios = new FillScenarios(
|
||||||
|
provider,
|
||||||
|
userAddresses,
|
||||||
|
contractAddresses.zrxToken,
|
||||||
|
contractAddresses.exchange,
|
||||||
|
contractAddresses.erc20Proxy,
|
||||||
|
contractAddresses.erc721Proxy,
|
||||||
|
);
|
||||||
|
[makerAddress, takerAddress, feeRecipient] = userAddresses;
|
||||||
|
const [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
|
||||||
|
[makerAssetData, takerAssetData, wethAssetData] = [
|
||||||
|
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
|
||||||
|
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
|
||||||
|
assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
after(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
// This constructor has incorrect types
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
describe('getSmartContractParamsOrThrow', () => {
|
||||||
|
|
||||||
|
describe('validation', () => {
|
||||||
|
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
|
||||||
|
const invalidSignedOrders = getSignedOrdersWithNoFees(
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
FILLABLE_AMOUNTS,
|
||||||
|
);
|
||||||
|
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, invalidSignedOrders);
|
||||||
|
const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, {});
|
||||||
|
// TODO(dave4506) finish up testing/coverage
|
||||||
|
// expect(
|
||||||
|
// swapQuoteConsumer.getSmartContractParamsOrThrow(invalidSwapQuote, {}),
|
||||||
|
// ).to.throws();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('valid swap quote', async () => {
|
||||||
|
it('provide correct smart contract params with default options', async () => {
|
||||||
|
const signedOrders = getSignedOrdersWithNoFees(
|
||||||
|
makerAssetData,
|
||||||
|
wethAssetData,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
FILLABLE_AMOUNTS,
|
||||||
|
);
|
||||||
|
const swapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, signedOrders);
|
||||||
|
const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { networkId });
|
||||||
|
const smartContractParamsInfo = swapQuoteConsumer.getSmartContractParamsOrThrow(swapQuote, {});
|
||||||
|
// console.log(smartContractParamsInfo);
|
||||||
|
// TODO(dave4506): Add elaborate testing
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCalldataOrThrow', () => {
|
||||||
|
|
||||||
|
describe('validation', () => {
|
||||||
|
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
|
||||||
|
const invalidSignedOrders = getSignedOrdersWithNoFees(
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
FILLABLE_AMOUNTS,
|
||||||
|
);
|
||||||
|
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, invalidSignedOrders);
|
||||||
|
const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, {});
|
||||||
|
// TODO(dave4506) finish up testing/coverage
|
||||||
|
// expect(
|
||||||
|
// swapQuoteConsumer.getSmartContractParamsOrThrow(invalidSwapQuote, {}),
|
||||||
|
// ).to.throws();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('valid swap quote', async () => {
|
||||||
|
it('provide correct calldata hex with default options', async () => {
|
||||||
|
const signedOrders = getSignedOrdersWithNoFees(
|
||||||
|
makerAssetData,
|
||||||
|
wethAssetData,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
FILLABLE_AMOUNTS,
|
||||||
|
);
|
||||||
|
const swapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, signedOrders);
|
||||||
|
const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { networkId });
|
||||||
|
const callDataInfo = swapQuoteConsumer.getCalldataOrThrow(swapQuote, {});
|
||||||
|
// console.log(callDataInfo);
|
||||||
|
// TODO(dave4506): Add elaborate testing
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
6
packages/asset-buyer/test/global_hooks.ts
Normal file
6
packages/asset-buyer/test/global_hooks.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
before('set up mocha', async function(): Promise<void> {
|
||||||
|
// HACK: Since the migrations take longer then our global mocha timeout limit
|
||||||
|
// we manually increase it for this before hook.
|
||||||
|
const mochaTestTimeoutMs = 25000;
|
||||||
|
this.timeout(mochaTestTimeoutMs); // tslint:disable-line:no-invalid-this
|
||||||
|
});
|
@ -5,8 +5,8 @@ import * as chai from 'chai';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types';
|
import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types';
|
||||||
import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
|
import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator';
|
||||||
|
|
||||||
import { chaiSetup } from './utils/chai_setup';
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
import { testHelpers } from './utils/test_helpers';
|
import { testHelpers } from './utils/test_helpers';
|
||||||
@ -15,7 +15,7 @@ chaiSetup.configure();
|
|||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
describe('buyQuoteCalculator', () => {
|
describe('swapQuoteCalculator', () => {
|
||||||
describe('#calculate', () => {
|
describe('#calculate', () => {
|
||||||
let firstOrder: SignedOrder;
|
let firstOrder: SignedOrder;
|
||||||
let firstRemainingFillAmount: BigNumber;
|
let firstRemainingFillAmount: BigNumber;
|
||||||
@ -71,12 +71,11 @@ describe('buyQuoteCalculator', () => {
|
|||||||
it('should throw if not enough maker asset liquidity (multiple orders)', () => {
|
it('should throw if not enough maker asset liquidity (multiple orders)', () => {
|
||||||
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(500),
|
new BigNumber(500),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -85,11 +84,10 @@ describe('buyQuoteCalculator', () => {
|
|||||||
it('should throw if not enough maker asset liquidity (multiple orders with 20% slippage)', () => {
|
it('should throw if not enough maker asset liquidity (multiple orders with 20% slippage)', () => {
|
||||||
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(500),
|
new BigNumber(500),
|
||||||
0,
|
|
||||||
0.2,
|
0.2,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -99,11 +97,10 @@ describe('buyQuoteCalculator', () => {
|
|||||||
it('should throw if not enough maker asset liquidity (multiple orders with 5% slippage)', () => {
|
it('should throw if not enough maker asset liquidity (multiple orders with 5% slippage)', () => {
|
||||||
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(600),
|
new BigNumber(600),
|
||||||
0,
|
|
||||||
0.05,
|
0.05,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -117,12 +114,11 @@ describe('buyQuoteCalculator', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
firstOrderAndFillableAmount,
|
firstOrderAndFillableAmount,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(201),
|
new BigNumber(201),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -139,12 +135,11 @@ describe('buyQuoteCalculator', () => {
|
|||||||
remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount],
|
remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount],
|
||||||
};
|
};
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
completelyFillableOrdersAndFillableAmount,
|
completelyFillableOrdersAndFillableAmount,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(124),
|
new BigNumber(124),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -157,12 +152,11 @@ describe('buyQuoteCalculator', () => {
|
|||||||
takerFee: new BigNumber(0),
|
takerFee: new BigNumber(0),
|
||||||
});
|
});
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(600),
|
new BigNumber(600),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -175,11 +169,10 @@ describe('buyQuoteCalculator', () => {
|
|||||||
takerFee: new BigNumber(0),
|
takerFee: new BigNumber(0),
|
||||||
});
|
});
|
||||||
const errorFunction = () => {
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(600),
|
new BigNumber(600),
|
||||||
0,
|
|
||||||
0.2,
|
0.2,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -189,12 +182,11 @@ describe('buyQuoteCalculator', () => {
|
|||||||
});
|
});
|
||||||
it('should not throw if order is fillable', () => {
|
it('should not throw if order is fillable', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
allFeeOrdersAndFillableAmounts,
|
allFeeOrdersAndFillableAmounts,
|
||||||
new BigNumber(300),
|
new BigNumber(300),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
).to.not.throw();
|
).to.not.throw();
|
||||||
@ -202,94 +194,102 @@ describe('buyQuoteCalculator', () => {
|
|||||||
it('should throw if not enough ZRX liquidity', () => {
|
it('should throw if not enough ZRX liquidity', () => {
|
||||||
// we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available
|
// we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available
|
||||||
expect(() =>
|
expect(() =>
|
||||||
buyQuoteCalculator.calculate(
|
swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
new BigNumber(300),
|
new BigNumber(300),
|
||||||
0,
|
0,
|
||||||
0,
|
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
|
).to.throw(SwapQuoterError.InsufficientZrxLiquidity);
|
||||||
});
|
});
|
||||||
it('calculates a correct buyQuote with no slippage', () => {
|
it('calculates a correct swapQuote with no slippage', () => {
|
||||||
// we request 200 makerAsset units which can be filled using the first order
|
// we request 200 makerAsset units which can be filled using the first order
|
||||||
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
|
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
|
||||||
const assetBuyAmount = new BigNumber(200);
|
const assetBuyAmount = new BigNumber(200);
|
||||||
const feePercentage = 0.02;
|
|
||||||
const slippagePercentage = 0;
|
const slippagePercentage = 0;
|
||||||
const buyQuote = buyQuoteCalculator.calculate(
|
const swapQuote = swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
assetBuyAmount,
|
assetBuyAmount,
|
||||||
feePercentage,
|
|
||||||
slippagePercentage,
|
slippagePercentage,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
|
expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
|
||||||
expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
|
expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
// 50 eth to fill the first order + 100 eth for fees
|
// 50 eth to fill the first order + 100 eth for fees
|
||||||
const expectedEthAmountForAsset = new BigNumber(50);
|
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
|
||||||
const expectedEthAmountForZrxFees = new BigNumber(100);
|
const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
|
||||||
const expectedFillEthAmount = expectedEthAmountForAsset;
|
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus(
|
||||||
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
|
expectedTakerAssetAmountForZrxFees,
|
||||||
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
|
);
|
||||||
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
|
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||||
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
expectedTakerAssetAmountForMakerAsset,
|
||||||
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
);
|
||||||
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||||
|
expectedTakerAssetAmountForZrxFees,
|
||||||
|
);
|
||||||
|
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
|
||||||
// because we have no slippage protection, minRate is equal to maxRate
|
// because we have no slippage protection, minRate is equal to maxRate
|
||||||
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||||
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
expectedTakerAssetAmountForMakerAsset,
|
||||||
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
);
|
||||||
// test if feePercentage gets passed through
|
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||||
expect(buyQuote.feePercentage).to.equal(feePercentage);
|
expectedTakerAssetAmountForZrxFees,
|
||||||
|
);
|
||||||
|
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
|
||||||
|
expectedTotalTakerAssetAmount,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('calculates a correct buyQuote with with slippage', () => {
|
it('calculates a correct swapQuote with with slippage', () => {
|
||||||
// we request 200 makerAsset units which can be filled using the first order
|
// we request 200 makerAsset units which can be filled using the first order
|
||||||
// however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits
|
// however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits
|
||||||
// so we need enough orders to fill 300 makerAssetUnits
|
// so we need enough orders to fill 300 makerAssetUnits
|
||||||
// 300 makerAssetUnits can only be filled using both orders
|
// 300 makerAssetUnits can only be filled using both orders
|
||||||
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
|
// the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
|
||||||
const assetBuyAmount = new BigNumber(200);
|
const assetBuyAmount = new BigNumber(200);
|
||||||
const feePercentage = 0.02;
|
|
||||||
const slippagePercentage = 0.5;
|
const slippagePercentage = 0.5;
|
||||||
const buyQuote = buyQuoteCalculator.calculate(
|
const swapQuote = swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
allFeeOrdersAndFillableAmounts,
|
allFeeOrdersAndFillableAmounts,
|
||||||
assetBuyAmount,
|
assetBuyAmount,
|
||||||
feePercentage,
|
|
||||||
slippagePercentage,
|
slippagePercentage,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
|
expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
|
||||||
expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
|
expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
// 50 eth to fill the first order + 100 eth for fees
|
// 50 eth to fill the first order + 100 eth for fees
|
||||||
const expectedEthAmountForAsset = new BigNumber(50);
|
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
|
||||||
const expectedEthAmountForZrxFees = new BigNumber(100);
|
const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
|
||||||
const expectedFillEthAmount = expectedEthAmountForAsset;
|
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus(
|
||||||
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
|
expectedTakerAssetAmountForZrxFees,
|
||||||
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
|
);
|
||||||
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
|
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||||
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
expectedTakerAssetAmountForMakerAsset,
|
||||||
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
);
|
||||||
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||||
|
expectedTakerAssetAmountForZrxFees,
|
||||||
|
);
|
||||||
|
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
|
||||||
// 100 eth to fill the first order + 208 eth for fees
|
// 100 eth to fill the first order + 208 eth for fees
|
||||||
const expectedWorstEthAmountForAsset = new BigNumber(100);
|
const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100);
|
||||||
const expectedWorstEthAmountForZrxFees = new BigNumber(208);
|
const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(208);
|
||||||
const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset;
|
const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset.plus(
|
||||||
const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.multipliedBy(feePercentage);
|
expectedWorstTakerAssetAmountForZrxFees,
|
||||||
const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees);
|
);
|
||||||
const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount);
|
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||||
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount);
|
expectedWorstTakerAssetAmountForMakerAsset,
|
||||||
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount);
|
);
|
||||||
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount);
|
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||||
// test if feePercentage gets passed through
|
expectedWorstTakerAssetAmountForZrxFees,
|
||||||
expect(buyQuote.feePercentage).to.equal(feePercentage);
|
);
|
||||||
|
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
|
||||||
|
expectedWorstTotalTakerAssetAmount,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -7,14 +7,15 @@ import * as chai from 'chai';
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
|
|
||||||
import { AssetBuyer } from '../src';
|
import { SwapQuoter } from '../src';
|
||||||
import { constants } from '../src/constants';
|
import { constants } from '../src/constants';
|
||||||
import { LiquidityForAssetData, OrderProvider, OrdersAndFillableAmounts } from '../src/types';
|
import { LiquidityForAssetData, OrderProvider, OrdersAndFillableAmounts } from '../src/types';
|
||||||
|
|
||||||
import { chaiSetup } from './utils/chai_setup';
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
import {
|
import {
|
||||||
mockAvailableAssetDatas,
|
mockAvailableMakerAssetDatas,
|
||||||
mockedAssetBuyerWithOrdersAndFillableAmounts,
|
mockAvailableTakerAssetDatas,
|
||||||
|
mockedSwapQuoterWithOrdersAndFillableAmounts,
|
||||||
orderProviderMock,
|
orderProviderMock,
|
||||||
} from './utils/mocks';
|
} from './utils/mocks';
|
||||||
|
|
||||||
@ -22,7 +23,8 @@ chaiSetup.configure();
|
|||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
const FAKE_SRA_URL = 'https://fakeurl.com';
|
const FAKE_SRA_URL = 'https://fakeurl.com';
|
||||||
const FAKE_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48';
|
const FAKE_TAKER_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48';
|
||||||
|
const FAKE_MAKER_ASSET_DATA = '0xf47261b00000000000000000000000009f5B0C7e1623793bF0620569b9749e79DF6D0bC5';
|
||||||
const TOKEN_DECIMALS = 18;
|
const TOKEN_DECIMALS = 18;
|
||||||
const DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"';
|
const DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"';
|
||||||
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||||
@ -38,19 +40,23 @@ const expectLiquidityResult = async (
|
|||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
expectedLiquidityResult: LiquidityForAssetData,
|
expectedLiquidityResult: LiquidityForAssetData,
|
||||||
) => {
|
) => {
|
||||||
const mockedAssetBuyer = mockedAssetBuyerWithOrdersAndFillableAmounts(
|
const mockedSwapQuoter = mockedSwapQuoterWithOrdersAndFillableAmounts(
|
||||||
web3Provider,
|
web3Provider,
|
||||||
orderProvider,
|
orderProvider,
|
||||||
FAKE_ASSET_DATA,
|
FAKE_MAKER_ASSET_DATA,
|
||||||
|
WETH_ASSET_DATA,
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
);
|
);
|
||||||
const liquidityResult = await mockedAssetBuyer.object.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
|
const liquidityResult = await mockedSwapQuoter.object.getLiquidityForMakerTakerAssetDataPairAsync(
|
||||||
|
FAKE_MAKER_ASSET_DATA,
|
||||||
|
WETH_ASSET_DATA,
|
||||||
|
);
|
||||||
expect(liquidityResult).to.deep.equal(expectedLiquidityResult);
|
expect(liquidityResult).to.deep.equal(expectedLiquidityResult);
|
||||||
};
|
};
|
||||||
|
|
||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
describe('AssetBuyer', () => {
|
describe('SwapQuoter', () => {
|
||||||
describe('getLiquidityForAssetDataAsync', () => {
|
describe('getLiquidityForMakerTakerAssetDataPairAsync', () => {
|
||||||
const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine);
|
const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine);
|
||||||
const mockOrderProvider = orderProviderMock();
|
const mockOrderProvider = orderProviderMock();
|
||||||
|
|
||||||
@ -65,40 +71,51 @@ describe('AssetBuyer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('validation', () => {
|
describe('validation', () => {
|
||||||
it('should ensure assetData is a string', async () => {
|
it('should ensure takerAssetData is a string', async () => {
|
||||||
const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(
|
const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(
|
||||||
mockWeb3Provider.object,
|
mockWeb3Provider.object,
|
||||||
FAKE_SRA_URL,
|
FAKE_SRA_URL,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(assetBuyer.getLiquidityForAssetDataAsync(false as any)).to.be.rejectedWith(
|
expect(
|
||||||
'Expected assetData to be of type string, encountered: false',
|
swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, false as any),
|
||||||
|
).to.be.rejectedWith('Expected takerAssetData to be of type string, encountered: false');
|
||||||
|
});
|
||||||
|
it('should ensure makerAssetData is a string', async () => {
|
||||||
|
const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(
|
||||||
|
mockWeb3Provider.object,
|
||||||
|
FAKE_SRA_URL,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(false as any, FAKE_TAKER_ASSET_DATA),
|
||||||
|
).to.be.rejectedWith('Expected makerAssetData to be of type string, encountered: false');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('asset pair not supported', () => {
|
describe('asset pair not supported', () => {
|
||||||
it('should return 0s when no asset pair not supported', async () => {
|
it('should return 0s when no asset pair are supported', async () => {
|
||||||
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, []);
|
mockAvailableMakerAssetDatas(mockOrderProvider, FAKE_TAKER_ASSET_DATA, []);
|
||||||
|
|
||||||
const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object);
|
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object);
|
||||||
const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
|
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA);
|
||||||
expect(liquidityResult).to.deep.equal({
|
expect(liquidityResult).to.deep.equal({
|
||||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
ethValueAvailableInWei: new BigNumber(0),
|
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 0s when only other asset pair supported', async () => {
|
it('should return 0s when only other asset pair supported', async () => {
|
||||||
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [DAI_ASSET_DATA]);
|
mockAvailableMakerAssetDatas(mockOrderProvider, FAKE_TAKER_ASSET_DATA, [DAI_ASSET_DATA]);
|
||||||
|
|
||||||
const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object);
|
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object);
|
||||||
const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
|
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA);
|
||||||
expect(liquidityResult).to.deep.equal({
|
expect(liquidityResult).to.deep.equal({
|
||||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
ethValueAvailableInWei: new BigNumber(0),
|
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('assetData is supported', () => {
|
describe('assetData is supported', () => {
|
||||||
// orders
|
// orders
|
||||||
@ -112,7 +129,7 @@ describe('AssetBuyer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [WETH_ASSET_DATA]);
|
mockAvailableMakerAssetDatas(mockOrderProvider, WETH_ASSET_DATA, [FAKE_MAKER_ASSET_DATA]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 0s when no orders available', async () => {
|
it('should return 0s when no orders available', async () => {
|
||||||
@ -121,8 +138,8 @@ describe('AssetBuyer', () => {
|
|||||||
remainingFillableMakerAssetAmounts: [],
|
remainingFillableMakerAssetAmounts: [],
|
||||||
};
|
};
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
ethValueAvailableInWei: new BigNumber(0),
|
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||||
};
|
};
|
||||||
await expectLiquidityResult(
|
await expectLiquidityResult(
|
||||||
mockWeb3Provider.object,
|
mockWeb3Provider.object,
|
||||||
@ -139,11 +156,12 @@ describe('AssetBuyer', () => {
|
|||||||
remainingFillableMakerAssetAmounts: orders.map(o => o.makerAssetAmount),
|
remainingFillableMakerAssetAmounts: orders.map(o => o.makerAssetAmount),
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
|
const expectedMakerTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
|
||||||
const expectedEthValueAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
|
const expectedTakerTokensAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
|
||||||
|
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
tokensAvailableInBaseUnits: expectedTokensAvailable,
|
makerTokensAvailableInBaseUnits: expectedMakerTokensAvailable,
|
||||||
ethValueAvailableInWei: expectedEthValueAvailable,
|
takerTokensAvailableInBaseUnits: expectedTakerTokensAvailable,
|
||||||
};
|
};
|
||||||
|
|
||||||
await expectLiquidityResult(
|
await expectLiquidityResult(
|
||||||
@ -159,9 +177,10 @@ describe('AssetBuyer', () => {
|
|||||||
orders: [sellTwoTokensFor1Weth],
|
orders: [sellTwoTokensFor1Weth],
|
||||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(1)],
|
remainingFillableMakerAssetAmounts: [baseUnitAmount(1)],
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
tokensAvailableInBaseUnits: baseUnitAmount(1),
|
makerTokensAvailableInBaseUnits: baseUnitAmount(1),
|
||||||
ethValueAvailableInWei: baseUnitAmount(0.5, WETH_DECIMALS),
|
takerTokensAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS),
|
||||||
};
|
};
|
||||||
|
|
||||||
await expectLiquidityResult(
|
await expectLiquidityResult(
|
||||||
@ -177,9 +196,10 @@ describe('AssetBuyer', () => {
|
|||||||
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
||||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(3)],
|
remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(3)],
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
tokensAvailableInBaseUnits: baseUnitAmount(4),
|
makerTokensAvailableInBaseUnits: baseUnitAmount(4),
|
||||||
ethValueAvailableInWei: baseUnitAmount(3.5, WETH_DECIMALS),
|
takerTokensAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS),
|
||||||
};
|
};
|
||||||
|
|
||||||
await expectLiquidityResult(
|
await expectLiquidityResult(
|
||||||
@ -195,9 +215,10 @@ describe('AssetBuyer', () => {
|
|||||||
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
||||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)],
|
remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)],
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedResult = {
|
const expectedResult = {
|
||||||
tokensAvailableInBaseUnits: baseUnitAmount(0),
|
makerTokensAvailableInBaseUnits: baseUnitAmount(0),
|
||||||
ethValueAvailableInWei: baseUnitAmount(0, WETH_DECIMALS),
|
takerTokensAvailableInBaseUnits: baseUnitAmount(0, WETH_DECIMALS),
|
||||||
};
|
};
|
||||||
|
|
||||||
await expectLiquidityResult(
|
await expectLiquidityResult(
|
18
packages/asset-buyer/test/utils/migrate.ts
Normal file
18
packages/asset-buyer/test/utils/migrate.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ContractAddresses } from '@0x/contract-addresses';
|
||||||
|
import { devConstants } from '@0x/dev-utils';
|
||||||
|
import { runMigrationsOnceAsync } from '@0x/migrations';
|
||||||
|
|
||||||
|
import { provider } from './web3_wrapper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures and runs the migrations exactly once. Any subsequent times this is
|
||||||
|
* called, it returns the cached addresses.
|
||||||
|
* @returns The addresses of contracts that were deployed during the migrations.
|
||||||
|
*/
|
||||||
|
export async function migrateOnceAsync(): Promise<ContractAddresses> {
|
||||||
|
const txDefaults = {
|
||||||
|
gas: devConstants.GAS_LIMIT,
|
||||||
|
from: devConstants.TESTRPC_FIRST_ADDRESS,
|
||||||
|
};
|
||||||
|
return runMigrationsOnceAsync(provider, txDefaults);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
|
|
||||||
import { SwapQuoter } from '../../src/asset_buyer';
|
import { SwapQuoter } from '../../src/swap_quoter';
|
||||||
import { OrderProvider, OrderProviderResponse, OrdersAndFillableAmounts } from '../../src/types';
|
import { OrderProvider, OrderProviderResponse, OrdersAndFillableAmounts } from '../../src/types';
|
||||||
|
|
||||||
// tslint:disable:promise-function-async
|
// tslint:disable:promise-function-async
|
||||||
@ -26,7 +26,7 @@ export const orderProviderMock = () => {
|
|||||||
return TypeMoq.Mock.ofType(OrderProviderClass, TypeMoq.MockBehavior.Strict);
|
return TypeMoq.Mock.ofType(OrderProviderClass, TypeMoq.MockBehavior.Strict);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockAvailableAssetDatas = (
|
export const mockAvailableMakerAssetDatas = (
|
||||||
mockOrderProvider: TypeMoq.IMock<OrderProviderClass>,
|
mockOrderProvider: TypeMoq.IMock<OrderProviderClass>,
|
||||||
assetData: string,
|
assetData: string,
|
||||||
availableAssetDatas: string[],
|
availableAssetDatas: string[],
|
||||||
@ -39,34 +39,49 @@ export const mockAvailableAssetDatas = (
|
|||||||
.verifiable(TypeMoq.Times.once());
|
.verifiable(TypeMoq.Times.once());
|
||||||
};
|
};
|
||||||
|
|
||||||
const partiallyMockedAssetBuyer = (
|
export const mockAvailableTakerAssetDatas = (
|
||||||
|
mockOrderProvider: TypeMoq.IMock<OrderProviderClass>,
|
||||||
|
assetData: string,
|
||||||
|
availableAssetDatas: string[],
|
||||||
|
) => {
|
||||||
|
mockOrderProvider
|
||||||
|
.setup(op => op.getAvailableTakerAssetDatasAsync(TypeMoq.It.isValue(assetData)))
|
||||||
|
.returns(() => {
|
||||||
|
return Promise.resolve(availableAssetDatas);
|
||||||
|
})
|
||||||
|
.verifiable(TypeMoq.Times.once());
|
||||||
|
};
|
||||||
|
|
||||||
|
const partiallyMockedSwapQuoter = (
|
||||||
provider: Web3ProviderEngine,
|
provider: Web3ProviderEngine,
|
||||||
orderProvider: OrderProvider,
|
orderProvider: OrderProvider,
|
||||||
): TypeMoq.IMock<SwapQuoter> => {
|
): TypeMoq.IMock<SwapQuoter> => {
|
||||||
const rawAssetBuyer = new SwapQuoter(provider, orderProvider);
|
const rawSwapQuoter = new SwapQuoter(provider, orderProvider);
|
||||||
const mockedAssetBuyer = TypeMoq.Mock.ofInstance(rawAssetBuyer, TypeMoq.MockBehavior.Loose, false);
|
const mockedSwapQuoter = TypeMoq.Mock.ofInstance(rawSwapQuoter, TypeMoq.MockBehavior.Loose, false);
|
||||||
mockedAssetBuyer.callBase = true;
|
mockedSwapQuoter.callBase = true;
|
||||||
return mockedAssetBuyer;
|
return mockedSwapQuoter;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockGetOrdersAndAvailableAmounts = (
|
const mockGetOrdersAndAvailableAmounts = (
|
||||||
mockedAssetBuyer: TypeMoq.IMock<SwapQuoter>,
|
mockedSwapQuoter: TypeMoq.IMock<SwapQuoter>,
|
||||||
assetData: string,
|
makerAssetData: string,
|
||||||
|
takerAssetData: string,
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
): void => {
|
): void => {
|
||||||
mockedAssetBuyer
|
mockedSwapQuoter
|
||||||
.setup(a => a.getOrdersAndFillableAmountsAsync(assetData, false))
|
.setup(a => a.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, false))
|
||||||
.returns(() => Promise.resolve(ordersAndFillableAmounts))
|
.returns(() => Promise.resolve(ordersAndFillableAmounts))
|
||||||
.verifiable(TypeMoq.Times.once());
|
.verifiable(TypeMoq.Times.once());
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockedAssetBuyerWithOrdersAndFillableAmounts = (
|
export const mockedSwapQuoterWithOrdersAndFillableAmounts = (
|
||||||
provider: Web3ProviderEngine,
|
provider: Web3ProviderEngine,
|
||||||
orderProvider: OrderProvider,
|
orderProvider: OrderProvider,
|
||||||
assetData: string,
|
makerAssetData: string,
|
||||||
|
takerAssetData: string,
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
): TypeMoq.IMock<SwapQuoter> => {
|
): TypeMoq.IMock<SwapQuoter> => {
|
||||||
const mockedAssetBuyer = partiallyMockedAssetBuyer(provider, orderProvider);
|
const mockedAssetQuoter = partiallyMockedSwapQuoter(provider, orderProvider);
|
||||||
mockGetOrdersAndAvailableAmounts(mockedAssetBuyer, assetData, ordersAndFillableAmounts);
|
mockGetOrdersAndAvailableAmounts(mockedAssetQuoter, makerAssetData, takerAssetData, ordersAndFillableAmounts);
|
||||||
return mockedAssetBuyer;
|
return mockedAssetQuoter;
|
||||||
};
|
};
|
||||||
|
41
packages/asset-buyer/test/utils/swap_quote.ts
Normal file
41
packages/asset-buyer/test/utils/swap_quote.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { FillScenarios } from '@0x/fill-scenarios';
|
||||||
|
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { SupportedProvider } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { SwapQuote } from '../../src';
|
||||||
|
|
||||||
|
const ZERO_BIG_NUMBER = new BigNumber(0);
|
||||||
|
|
||||||
|
export const getSignedOrdersWithNoFees = (makerAssetData: string, takerAssetData: string, makerAddress: string, takerAddress: string, fillableAmounts: BigNumber[]): SignedOrder[] => {
|
||||||
|
return _.map(fillableAmounts, (fillableAmount: BigNumber) => orderFactory.createSignedOrderFromPartial(
|
||||||
|
{
|
||||||
|
makerAddress,
|
||||||
|
makerAssetAmount: fillableAmount,
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetAmount: fillableAmount,
|
||||||
|
takerAssetData,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFullyFillableSwapQuoteWithNoFees = (makerAssetData: string, takerAssetData: string, orders: SignedOrder[]): SwapQuote => {
|
||||||
|
const makerAssetFillAmount = _.reduce(orders, (a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount), ZERO_BIG_NUMBER);
|
||||||
|
const totalTakerTokenAmount = _.reduce(orders, (a: BigNumber, c: SignedOrder) => a.plus(c.takerAssetAmount), ZERO_BIG_NUMBER);
|
||||||
|
const quoteInfo = {
|
||||||
|
takerTokenAmount: totalTakerTokenAmount,
|
||||||
|
feeTakerTokenAmount: ZERO_BIG_NUMBER,
|
||||||
|
totalTakerTokenAmount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
orders,
|
||||||
|
feeOrders: [],
|
||||||
|
makerAssetFillAmount,
|
||||||
|
bestCaseQuoteInfo: quoteInfo,
|
||||||
|
worstCaseQuoteInfo: quoteInfo,
|
||||||
|
};
|
||||||
|
};
|
8
packages/asset-buyer/test/utils/web3_wrapper.ts
Normal file
8
packages/asset-buyer/test/utils/web3_wrapper.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { web3Factory } from '@0x/dev-utils';
|
||||||
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
|
||||||
|
const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true });
|
||||||
|
const web3Wrapper = new Web3Wrapper(provider);
|
||||||
|
|
||||||
|
export { provider, web3Wrapper };
|
Loading…
x
Reference in New Issue
Block a user