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",
|
||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||
"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",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
@ -39,8 +39,12 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^2.0.10",
|
||||
"@0x/connect": "^5.0.10",
|
||||
"@0x/contract-addresses": "^2.3.3",
|
||||
"@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/migrations": "^4.1.6",
|
||||
"@0x/order-utils": "^8.1.1",
|
||||
"@0x/subproviders": "^4.1.0",
|
||||
"@0x/types": "^2.2.2",
|
||||
|
@ -22,7 +22,6 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
||||
const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = {
|
||||
feePercentage: 0,
|
||||
feeRecipient: NULL_ADDRESS,
|
||||
ethAmount: new BigNumber(0),
|
||||
};
|
||||
|
||||
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 { 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 { BasicOrderProvider } from './order_providers/basic_order_provider';
|
||||
|
@ -22,21 +22,13 @@ import { assetDataUtils } from '../utils/asset_data_utils';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMarketBuySmartContractParams> {
|
||||
|
||||
public readonly provider: ZeroExProvider;
|
||||
public readonly networkId: number;
|
||||
|
||||
private readonly _contractWrappers: ContractWrappers;
|
||||
|
||||
constructor(
|
||||
supportedProvider: SupportedProvider,
|
||||
options: Partial<SwapQuoteConsumerOpts> = {},
|
||||
) {
|
||||
const { networkId } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
||||
options,
|
||||
);
|
||||
constructor(supportedProvider: SupportedProvider, options: Partial<SwapQuoteConsumerOpts> = {}) {
|
||||
const { networkId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
||||
assert.isNumber('networkId', networkId);
|
||||
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
@ -64,12 +56,16 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
||||
const calldataHexString = abiEncoder.encode(args);
|
||||
return {
|
||||
calldataHexString,
|
||||
methodAbi,
|
||||
to,
|
||||
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());
|
||||
|
||||
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
||||
@ -80,14 +76,19 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
||||
|
||||
assert.isNumber('feePercentage', unFormattedFeePercentage);
|
||||
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 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);
|
||||
|
||||
@ -101,7 +102,11 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
||||
feeRecipient,
|
||||
};
|
||||
|
||||
const methodAbi = utils.getMethodAbiFromContractAbi(this._contractWrappers.forwarder.abi, 'marketBuyOrdersWithEth') as MethodAbi;
|
||||
const methodAbi = utils.getMethodAbiFromContractAbi(
|
||||
this._contractWrappers.forwarder.abi,
|
||||
'marketBuyOrdersWithEth',
|
||||
) as MethodAbi;
|
||||
|
||||
return {
|
||||
params,
|
||||
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());
|
||||
|
||||
const { ethAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge(
|
||||
@ -121,8 +129,9 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer<ForwarderMa
|
||||
|
||||
assert.isNumber('feePercentage', feePercentage);
|
||||
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
||||
assert.isBigNumber('ethAmount', ethAmount);
|
||||
|
||||
if (ethAmount !== undefined) {
|
||||
assert.isBigNumber('ethAmount', ethAmount);
|
||||
}
|
||||
if (takerAddress !== undefined) {
|
||||
assert.isETHAddressHex('takerAddress', takerAddress);
|
||||
}
|
||||
|
@ -89,7 +89,11 @@ export class 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(
|
||||
{},
|
||||
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
||||
@ -202,7 +206,7 @@ export class SwapQuoter {
|
||||
): Promise<LiquidityForAssetData> {
|
||||
const shouldForceOrderRefresh =
|
||||
options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
|
||||
assert.isString('makerAssetDataa', makerAssetData);
|
||||
assert.isString('makerAssetData', makerAssetData);
|
||||
assert.isString('takerAssetData', takerAssetData);
|
||||
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
|
||||
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
|
@ -47,6 +47,7 @@ export interface OrderProvider {
|
||||
*/
|
||||
export interface CalldataInfo {
|
||||
calldataHexString: string;
|
||||
methodAbi: MethodAbi;
|
||||
to: string;
|
||||
ethAmount?: BigNumber;
|
||||
}
|
||||
@ -128,14 +129,13 @@ export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts {
|
||||
export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts {
|
||||
feePercentage: number;
|
||||
feeRecipient: string;
|
||||
ethAmount: BigNumber;
|
||||
ethAmount?: BigNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
|
@ -11,7 +11,7 @@ export const swapQuoteCalculator = {
|
||||
calculate(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
makerAssetBuyAmount: BigNumber,
|
||||
makerAssetFillAmount: BigNumber,
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
): SwapQuote {
|
||||
@ -19,20 +19,20 @@ export const swapQuoteCalculator = {
|
||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||
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)
|
||||
const {
|
||||
resultOrders,
|
||||
remainingFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts,
|
||||
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetBuyAmount, {
|
||||
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetFillAmount, {
|
||||
remainingFillableMakerAssetAmounts,
|
||||
slippageBufferAmount,
|
||||
});
|
||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
// 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);
|
||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||
// in order to get the total amount needed considering slippage
|
||||
@ -87,21 +87,21 @@ export const swapQuoteCalculator = {
|
||||
const bestCaseQuoteInfo = calculateQuoteInfo(
|
||||
trimmedOrdersAndFillableAmounts,
|
||||
trimmedFeeOrdersAndFillableAmounts,
|
||||
makerAssetBuyAmount,
|
||||
makerAssetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||
const worstCaseQuoteInfo = calculateQuoteInfo(
|
||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||
makerAssetBuyAmount,
|
||||
makerAssetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
|
||||
return {
|
||||
takerAssetData,
|
||||
makerAssetData,
|
||||
makerAssetBuyAmount,
|
||||
makerAssetFillAmount,
|
||||
orders: resultOrders,
|
||||
feeOrders: resultFeeOrders,
|
||||
bestCaseQuoteInfo,
|
||||
@ -191,30 +191,30 @@ function findTakerTokenAmountNeededToBuyZrx(
|
||||
|
||||
function findTakerTokenAndZrxAmountNeededToBuyAsset(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
makerAssetBuyAmount: BigNumber,
|
||||
makerAssetFillAmount: BigNumber,
|
||||
): [BigNumber, BigNumber] {
|
||||
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
|
||||
const result = _.reduce(
|
||||
orders,
|
||||
(acc, order, index) => {
|
||||
const { totalTakerTokenAmount, totalZrxAmount, remainingMakerAssetBuyAmount } = acc;
|
||||
const { totalTakerTokenAmount, totalZrxAmount, remainingmakerAssetFillAmount } = acc;
|
||||
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 takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount);
|
||||
return {
|
||||
totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount),
|
||||
totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
|
||||
remainingMakerAssetBuyAmount: BigNumber.max(
|
||||
remainingmakerAssetFillAmount: BigNumber.max(
|
||||
constants.ZERO_AMOUNT,
|
||||
remainingMakerAssetBuyAmount.minus(makerFillAmount),
|
||||
remainingmakerAssetFillAmount.minus(makerFillAmount),
|
||||
),
|
||||
};
|
||||
},
|
||||
{
|
||||
totalTakerTokenAmount: constants.ZERO_AMOUNT,
|
||||
totalZrxAmount: constants.ZERO_AMOUNT,
|
||||
remainingMakerAssetBuyAmount: makerAssetBuyAmount,
|
||||
remainingmakerAssetFillAmount: makerAssetFillAmount,
|
||||
},
|
||||
);
|
||||
return [result.totalTakerTokenAmount, result.totalZrxAmount];
|
||||
|
@ -15,7 +15,7 @@ export const utils = {
|
||||
return _.find(
|
||||
abi,
|
||||
(def: AbiDefinition): boolean => {
|
||||
if (def.type === `'function'`) {
|
||||
if (def.type === 'function') {
|
||||
const methodDef = def as MethodAbi;
|
||||
return methodDef.name === name;
|
||||
} 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 'mocha';
|
||||
|
||||
import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types';
|
||||
import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
|
||||
import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types';
|
||||
import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { testHelpers } from './utils/test_helpers';
|
||||
@ -15,7 +15,7 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
describe('buyQuoteCalculator', () => {
|
||||
describe('swapQuoteCalculator', () => {
|
||||
describe('#calculate', () => {
|
||||
let firstOrder: SignedOrder;
|
||||
let firstRemainingFillAmount: BigNumber;
|
||||
@ -71,12 +71,11 @@ describe('buyQuoteCalculator', () => {
|
||||
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
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(500),
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
};
|
||||
@ -85,11 +84,10 @@ describe('buyQuoteCalculator', () => {
|
||||
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
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(500),
|
||||
0,
|
||||
0.2,
|
||||
false,
|
||||
);
|
||||
@ -99,11 +97,10 @@ describe('buyQuoteCalculator', () => {
|
||||
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
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(600),
|
||||
0,
|
||||
0.05,
|
||||
false,
|
||||
);
|
||||
@ -117,12 +114,11 @@ describe('buyQuoteCalculator', () => {
|
||||
};
|
||||
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
firstOrderAndFillableAmount,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(201),
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
};
|
||||
@ -139,12 +135,11 @@ describe('buyQuoteCalculator', () => {
|
||||
remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount],
|
||||
};
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
completelyFillableOrdersAndFillableAmount,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(124),
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
};
|
||||
@ -157,12 +152,11 @@ describe('buyQuoteCalculator', () => {
|
||||
takerFee: new BigNumber(0),
|
||||
});
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(600),
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
};
|
||||
@ -175,11 +169,10 @@ describe('buyQuoteCalculator', () => {
|
||||
takerFee: new BigNumber(0),
|
||||
});
|
||||
const errorFunction = () => {
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
{ orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] },
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(600),
|
||||
0,
|
||||
0.2,
|
||||
false,
|
||||
);
|
||||
@ -189,12 +182,11 @@ describe('buyQuoteCalculator', () => {
|
||||
});
|
||||
it('should not throw if order is fillable', () => {
|
||||
expect(() =>
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
allFeeOrdersAndFillableAmounts,
|
||||
new BigNumber(300),
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
),
|
||||
).to.not.throw();
|
||||
@ -202,94 +194,102 @@ describe('buyQuoteCalculator', () => {
|
||||
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
|
||||
expect(() =>
|
||||
buyQuoteCalculator.calculate(
|
||||
swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
new BigNumber(300),
|
||||
0,
|
||||
0,
|
||||
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
|
||||
// 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 feePercentage = 0.02;
|
||||
const slippagePercentage = 0;
|
||||
const buyQuote = buyQuoteCalculator.calculate(
|
||||
const swapQuote = swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
smallFeeOrderAndFillableAmount,
|
||||
assetBuyAmount,
|
||||
feePercentage,
|
||||
slippagePercentage,
|
||||
false,
|
||||
);
|
||||
// test if orders are correct
|
||||
expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
|
||||
expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
|
||||
expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
|
||||
expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
|
||||
// test if rates are correct
|
||||
// 50 eth to fill the first order + 100 eth for fees
|
||||
const expectedEthAmountForAsset = new BigNumber(50);
|
||||
const expectedEthAmountForZrxFees = new BigNumber(100);
|
||||
const expectedFillEthAmount = expectedEthAmountForAsset;
|
||||
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
|
||||
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
|
||||
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
||||
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
|
||||
const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
|
||||
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus(
|
||||
expectedTakerAssetAmountForZrxFees,
|
||||
);
|
||||
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||
expectedTakerAssetAmountForMakerAsset,
|
||||
);
|
||||
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
|
||||
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
||||
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
||||
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
||||
// test if feePercentage gets passed through
|
||||
expect(buyQuote.feePercentage).to.equal(feePercentage);
|
||||
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||
expectedTakerAssetAmountForMakerAsset,
|
||||
);
|
||||
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||
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
|
||||
// however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits
|
||||
// so we need enough orders to fill 300 makerAssetUnits
|
||||
// 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
|
||||
const assetBuyAmount = new BigNumber(200);
|
||||
const feePercentage = 0.02;
|
||||
const slippagePercentage = 0.5;
|
||||
const buyQuote = buyQuoteCalculator.calculate(
|
||||
const swapQuote = swapQuoteCalculator.calculate(
|
||||
ordersAndFillableAmounts,
|
||||
allFeeOrdersAndFillableAmounts,
|
||||
assetBuyAmount,
|
||||
feePercentage,
|
||||
slippagePercentage,
|
||||
false,
|
||||
);
|
||||
// test if orders are correct
|
||||
expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
|
||||
expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
|
||||
expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
|
||||
expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
|
||||
// test if rates are correct
|
||||
// 50 eth to fill the first order + 100 eth for fees
|
||||
const expectedEthAmountForAsset = new BigNumber(50);
|
||||
const expectedEthAmountForZrxFees = new BigNumber(100);
|
||||
const expectedFillEthAmount = expectedEthAmountForAsset;
|
||||
const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage);
|
||||
const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees);
|
||||
const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
|
||||
expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
|
||||
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
|
||||
const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
|
||||
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus(
|
||||
expectedTakerAssetAmountForZrxFees,
|
||||
);
|
||||
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||
expectedTakerAssetAmountForMakerAsset,
|
||||
);
|
||||
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
|
||||
const expectedWorstEthAmountForAsset = new BigNumber(100);
|
||||
const expectedWorstEthAmountForZrxFees = new BigNumber(208);
|
||||
const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset;
|
||||
const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.multipliedBy(feePercentage);
|
||||
const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees);
|
||||
const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount);
|
||||
expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount);
|
||||
expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount);
|
||||
expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount);
|
||||
// test if feePercentage gets passed through
|
||||
expect(buyQuote.feePercentage).to.equal(feePercentage);
|
||||
const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100);
|
||||
const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(208);
|
||||
const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset.plus(
|
||||
expectedWorstTakerAssetAmountForZrxFees,
|
||||
);
|
||||
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
|
||||
expectedWorstTakerAssetAmountForMakerAsset,
|
||||
);
|
||||
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
|
||||
expectedWorstTakerAssetAmountForZrxFees,
|
||||
);
|
||||
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
|
||||
expectedWorstTotalTakerAssetAmount,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -7,14 +7,15 @@ import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
|
||||
import { AssetBuyer } from '../src';
|
||||
import { SwapQuoter } from '../src';
|
||||
import { constants } from '../src/constants';
|
||||
import { LiquidityForAssetData, OrderProvider, OrdersAndFillableAmounts } from '../src/types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import {
|
||||
mockAvailableAssetDatas,
|
||||
mockedAssetBuyerWithOrdersAndFillableAmounts,
|
||||
mockAvailableMakerAssetDatas,
|
||||
mockAvailableTakerAssetDatas,
|
||||
mockedSwapQuoterWithOrdersAndFillableAmounts,
|
||||
orderProviderMock,
|
||||
} from './utils/mocks';
|
||||
|
||||
@ -22,7 +23,8 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
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 DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"';
|
||||
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||
@ -38,19 +40,23 @@ const expectLiquidityResult = async (
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
expectedLiquidityResult: LiquidityForAssetData,
|
||||
) => {
|
||||
const mockedAssetBuyer = mockedAssetBuyerWithOrdersAndFillableAmounts(
|
||||
const mockedSwapQuoter = mockedSwapQuoterWithOrdersAndFillableAmounts(
|
||||
web3Provider,
|
||||
orderProvider,
|
||||
FAKE_ASSET_DATA,
|
||||
FAKE_MAKER_ASSET_DATA,
|
||||
WETH_ASSET_DATA,
|
||||
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);
|
||||
};
|
||||
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
describe('AssetBuyer', () => {
|
||||
describe('getLiquidityForAssetDataAsync', () => {
|
||||
describe('SwapQuoter', () => {
|
||||
describe('getLiquidityForMakerTakerAssetDataPairAsync', () => {
|
||||
const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine);
|
||||
const mockOrderProvider = orderProviderMock();
|
||||
|
||||
@ -65,40 +71,51 @@ describe('AssetBuyer', () => {
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
it('should ensure assetData is a string', async () => {
|
||||
const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(
|
||||
it('should ensure takerAssetData is a string', async () => {
|
||||
const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(
|
||||
mockWeb3Provider.object,
|
||||
FAKE_SRA_URL,
|
||||
);
|
||||
|
||||
expect(assetBuyer.getLiquidityForAssetDataAsync(false as any)).to.be.rejectedWith(
|
||||
'Expected assetData to be of type string, encountered: false',
|
||||
expect(
|
||||
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', () => {
|
||||
it('should return 0s when no asset pair not supported', async () => {
|
||||
mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, []);
|
||||
it('should return 0s when no asset pair are supported', async () => {
|
||||
mockAvailableMakerAssetDatas(mockOrderProvider, FAKE_TAKER_ASSET_DATA, []);
|
||||
|
||||
const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object);
|
||||
const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
|
||||
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object);
|
||||
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA);
|
||||
expect(liquidityResult).to.deep.equal({
|
||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
||||
ethValueAvailableInWei: new BigNumber(0),
|
||||
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
});
|
||||
});
|
||||
|
||||
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 liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA);
|
||||
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object);
|
||||
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA);
|
||||
expect(liquidityResult).to.deep.equal({
|
||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
||||
ethValueAvailableInWei: new BigNumber(0),
|
||||
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('assetData is supported', () => {
|
||||
// orders
|
||||
@ -112,7 +129,7 @@ describe('AssetBuyer', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
@ -121,8 +138,8 @@ describe('AssetBuyer', () => {
|
||||
remainingFillableMakerAssetAmounts: [],
|
||||
};
|
||||
const expectedResult = {
|
||||
tokensAvailableInBaseUnits: new BigNumber(0),
|
||||
ethValueAvailableInWei: new BigNumber(0),
|
||||
makerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
takerTokensAvailableInBaseUnits: new BigNumber(0),
|
||||
};
|
||||
await expectLiquidityResult(
|
||||
mockWeb3Provider.object,
|
||||
@ -139,11 +156,12 @@ describe('AssetBuyer', () => {
|
||||
remainingFillableMakerAssetAmounts: orders.map(o => o.makerAssetAmount),
|
||||
};
|
||||
|
||||
const expectedTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
|
||||
const expectedEthValueAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
|
||||
const expectedMakerTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
|
||||
const expectedTakerTokensAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
|
||||
|
||||
const expectedResult = {
|
||||
tokensAvailableInBaseUnits: expectedTokensAvailable,
|
||||
ethValueAvailableInWei: expectedEthValueAvailable,
|
||||
makerTokensAvailableInBaseUnits: expectedMakerTokensAvailable,
|
||||
takerTokensAvailableInBaseUnits: expectedTakerTokensAvailable,
|
||||
};
|
||||
|
||||
await expectLiquidityResult(
|
||||
@ -159,9 +177,10 @@ describe('AssetBuyer', () => {
|
||||
orders: [sellTwoTokensFor1Weth],
|
||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(1)],
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
tokensAvailableInBaseUnits: baseUnitAmount(1),
|
||||
ethValueAvailableInWei: baseUnitAmount(0.5, WETH_DECIMALS),
|
||||
makerTokensAvailableInBaseUnits: baseUnitAmount(1),
|
||||
takerTokensAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS),
|
||||
};
|
||||
|
||||
await expectLiquidityResult(
|
||||
@ -177,9 +196,10 @@ describe('AssetBuyer', () => {
|
||||
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(3)],
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
tokensAvailableInBaseUnits: baseUnitAmount(4),
|
||||
ethValueAvailableInWei: baseUnitAmount(3.5, WETH_DECIMALS),
|
||||
makerTokensAvailableInBaseUnits: baseUnitAmount(4),
|
||||
takerTokensAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS),
|
||||
};
|
||||
|
||||
await expectLiquidityResult(
|
||||
@ -195,9 +215,10 @@ describe('AssetBuyer', () => {
|
||||
orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth],
|
||||
remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)],
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
tokensAvailableInBaseUnits: baseUnitAmount(0),
|
||||
ethValueAvailableInWei: baseUnitAmount(0, WETH_DECIMALS),
|
||||
makerTokensAvailableInBaseUnits: baseUnitAmount(0),
|
||||
takerTokensAvailableInBaseUnits: baseUnitAmount(0, WETH_DECIMALS),
|
||||
};
|
||||
|
||||
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 * as TypeMoq from 'typemoq';
|
||||
|
||||
import { SwapQuoter } from '../../src/asset_buyer';
|
||||
import { SwapQuoter } from '../../src/swap_quoter';
|
||||
import { OrderProvider, OrderProviderResponse, OrdersAndFillableAmounts } from '../../src/types';
|
||||
|
||||
// tslint:disable:promise-function-async
|
||||
@ -26,7 +26,7 @@ export const orderProviderMock = () => {
|
||||
return TypeMoq.Mock.ofType(OrderProviderClass, TypeMoq.MockBehavior.Strict);
|
||||
};
|
||||
|
||||
export const mockAvailableAssetDatas = (
|
||||
export const mockAvailableMakerAssetDatas = (
|
||||
mockOrderProvider: TypeMoq.IMock<OrderProviderClass>,
|
||||
assetData: string,
|
||||
availableAssetDatas: string[],
|
||||
@ -39,34 +39,49 @@ export const mockAvailableAssetDatas = (
|
||||
.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,
|
||||
orderProvider: OrderProvider,
|
||||
): TypeMoq.IMock<SwapQuoter> => {
|
||||
const rawAssetBuyer = new SwapQuoter(provider, orderProvider);
|
||||
const mockedAssetBuyer = TypeMoq.Mock.ofInstance(rawAssetBuyer, TypeMoq.MockBehavior.Loose, false);
|
||||
mockedAssetBuyer.callBase = true;
|
||||
return mockedAssetBuyer;
|
||||
const rawSwapQuoter = new SwapQuoter(provider, orderProvider);
|
||||
const mockedSwapQuoter = TypeMoq.Mock.ofInstance(rawSwapQuoter, TypeMoq.MockBehavior.Loose, false);
|
||||
mockedSwapQuoter.callBase = true;
|
||||
return mockedSwapQuoter;
|
||||
};
|
||||
|
||||
const mockGetOrdersAndAvailableAmounts = (
|
||||
mockedAssetBuyer: TypeMoq.IMock<SwapQuoter>,
|
||||
assetData: string,
|
||||
mockedSwapQuoter: TypeMoq.IMock<SwapQuoter>,
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
): void => {
|
||||
mockedAssetBuyer
|
||||
.setup(a => a.getOrdersAndFillableAmountsAsync(assetData, false))
|
||||
mockedSwapQuoter
|
||||
.setup(a => a.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, false))
|
||||
.returns(() => Promise.resolve(ordersAndFillableAmounts))
|
||||
.verifiable(TypeMoq.Times.once());
|
||||
};
|
||||
|
||||
export const mockedAssetBuyerWithOrdersAndFillableAmounts = (
|
||||
export const mockedSwapQuoterWithOrdersAndFillableAmounts = (
|
||||
provider: Web3ProviderEngine,
|
||||
orderProvider: OrderProvider,
|
||||
assetData: string,
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
): TypeMoq.IMock<SwapQuoter> => {
|
||||
const mockedAssetBuyer = partiallyMockedAssetBuyer(provider, orderProvider);
|
||||
mockGetOrdersAndAvailableAmounts(mockedAssetBuyer, assetData, ordersAndFillableAmounts);
|
||||
return mockedAssetBuyer;
|
||||
const mockedAssetQuoter = partiallyMockedSwapQuoter(provider, orderProvider);
|
||||
mockGetOrdersAndAvailableAmounts(mockedAssetQuoter, makerAssetData, takerAssetData, ordersAndFillableAmounts);
|
||||
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