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:
David Sun 2019-06-19 17:08:12 -07:00
parent 222f7e6fd4
commit e1ab9aa690
16 changed files with 430 additions and 162 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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';

View File

@ -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);
}

View File

@ -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);

View File

@ -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).

View File

@ -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];

View File

@ -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 {

View 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
});
});
});
});

View 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
});

View File

@ -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,
);
});
});
});

View File

@ -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(

View 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);
}

View File

@ -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;
};

View 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,
};
};

View 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 };