diff options
Diffstat (limited to 'packages/order-utils/test')
-rw-r--r-- | packages/order-utils/test/market_utils_test.ts | 280 | ||||
-rw-r--r-- | packages/order-utils/test/utils/test_order_factory.ts | 32 |
2 files changed, 312 insertions, 0 deletions
diff --git a/packages/order-utils/test/market_utils_test.ts b/packages/order-utils/test/market_utils_test.ts new file mode 100644 index 000000000..21c0a4802 --- /dev/null +++ b/packages/order-utils/test/market_utils_test.ts @@ -0,0 +1,280 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { constants, marketUtils } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrderFactory } from './utils/test_order_factory'; + +chaiSetup.configure(); +const expect = chai.expect; + +// tslint:disable: no-unused-expression +describe('marketUtils', () => { + describe('#findOrdersThatCoverMakerAssetFillAmount', () => { + describe('no orders', () => { + it('returns empty and unchanged remainingFillAmount', async () => { + const fillAmount = new BigNumber(10); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + [], + [], + fillAmount, + ); + expect(resultOrders).to.be.empty; + expect(remainingFillAmount).to.be.bignumber.equal(fillAmount); + }); + }); + describe('orders are completely fillable', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + const makerAssetAmount = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount + const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; + it('returns input orders and zero remainingFillAmount when input exactly matches requested fill amount', async () => { + // try to fill 20 units of makerAsset + // include 10 units of slippageBufferAmount + const fillAmount = new BigNumber(20); + const slippageBufferAmount = new BigNumber(10); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and zero remainingFillAmount when input has more than requested fill amount', async () => { + // try to fill 15 units of makerAsset + // include 10 units of slippageBufferAmount + const fillAmount = new BigNumber(15); + const slippageBufferAmount = new BigNumber(10); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns input orders and non-zero remainingFillAmount when input has less than requested fill amount', async () => { + // try to fill 30 units of makerAsset + // include 5 units of slippageBufferAmount + const fillAmount = new BigNumber(30); + const slippageBufferAmount = new BigNumber(5); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + slippageBufferAmount, + ); + expect(resultOrders).to.be.deep.equal(inputOrders); + expect(remainingFillAmount).to.be.bignumber.equal(new BigNumber(5)); + }); + it('returns first order and zero remainingFillAmount when requested fill amount is exactly covered by the first order', async () => { + // try to fill 10 units of makerAsset + const fillAmount = new BigNumber(10); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('returns first two orders and zero remainingFillAmount when requested fill amount is over covered by the first two order', async () => { + // try to fill 15 units of makerAsset + const fillAmount = new BigNumber(15); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[0], inputOrders[1]]); + expect(remainingFillAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('orders are partially fillable', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + const makerAssetAmount = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that cover different partial fill scenarios + // 1. order is completely filled already + // 2. order is partially fillable + // 3. order is completely fillable + const remainingFillableMakerAssetAmounts = [constants.ZERO_AMOUNT, new BigNumber(5), makerAssetAmount]; + it('returns last two orders and non-zero remainingFillAmount when trying to fill original makerAssetAmounts', async () => { + // try to fill 30 units of makerAsset + const fillAmount = new BigNumber(30); + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( + inputOrders, + remainingFillableMakerAssetAmounts, + fillAmount, + ); + expect(resultOrders).to.be.deep.equal([inputOrders[1], inputOrders[2]]); + expect(remainingFillAmount).to.be.bignumber.equal(new BigNumber(15)); + }); + }); + }); + describe('#findFeeOrdersThatCoverFeesForTargetOrders', () => { + // generate three signed fee orders each with 10 units of ZRX, 30 total + const zrxAmount = new BigNumber(10); + const inputFeeOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount: zrxAmount, + }, + 3, + ); + // generate remainingFillableFeeAmounts that equal the zrxAmount + const remainingFillableFeeAmounts = [zrxAmount, zrxAmount, zrxAmount]; + describe('no target orders', () => { + it('returns empty and zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + [], + [], + inputFeeOrders, + remainingFillableFeeAmounts, + ); + expect(resultOrders).to.be.empty; + expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('no fee orders', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + // each signed order requires 10 units of takerFee + const makerAssetAmount = new BigNumber(10); + const takerFee = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + takerFee, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount + const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; + it('returns empty and non-zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + inputOrders, + remainingFillableMakerAssetAmounts, + [], + [], + ); + expect(resultOrders).to.be.empty; + expect(remainingFeeAmount).to.be.bignumber.equal(new BigNumber(30)); + }); + }); + describe('target orders have no fees', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + const makerAssetAmount = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount + const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; + it('returns empty and zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + inputOrders, + remainingFillableMakerAssetAmounts, + inputFeeOrders, + remainingFillableFeeAmounts, + ); + expect(resultOrders).to.be.empty; + expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('target orders require fees and are completely fillable', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + // each signed order requires 10 units of takerFee + const makerAssetAmount = new BigNumber(10); + const takerFee = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + takerFee, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount + const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; + it('returns input fee orders and zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + inputOrders, + remainingFillableMakerAssetAmounts, + inputFeeOrders, + remainingFillableFeeAmounts, + ); + expect(resultOrders).to.be.deep.equal(inputFeeOrders); + expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('target orders require fees and are partially fillable', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + // each signed order requires 10 units of takerFee + const makerAssetAmount = new BigNumber(10); + const takerFee = new BigNumber(10); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + takerFee, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that cover different partial fill scenarios + // 1. order is completely filled already + // 2. order is partially fillable + // 3. order is completely fillable + const remainingFillableMakerAssetAmounts = [constants.ZERO_AMOUNT, new BigNumber(5), makerAssetAmount]; + it('returns first two input fee orders and zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + inputOrders, + remainingFillableMakerAssetAmounts, + inputFeeOrders, + remainingFillableFeeAmounts, + ); + expect(resultOrders).to.be.deep.equal([inputFeeOrders[0], inputFeeOrders[1]]); + expect(remainingFeeAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + }); + describe('target orders require more fees than available', () => { + // generate three signed orders each with 10 units of makerAsset, 30 total + // each signed order requires 20 units of takerFee + const makerAssetAmount = new BigNumber(10); + const takerFee = new BigNumber(20); + const inputOrders = testOrderFactory.generateTestSignedOrders( + { + makerAssetAmount, + takerFee, + }, + 3, + ); + // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount + const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; + it('returns input fee orders and non-zero remainingFeeAmount', async () => { + const { resultOrders, remainingFeeAmount } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + inputOrders, + remainingFillableMakerAssetAmounts, + inputFeeOrders, + remainingFillableFeeAmounts, + ); + expect(resultOrders).to.be.deep.equal(inputFeeOrders); + expect(remainingFeeAmount).to.be.bignumber.equal(new BigNumber(30)); + }); + }); + }); +}); diff --git a/packages/order-utils/test/utils/test_order_factory.ts b/packages/order-utils/test/utils/test_order_factory.ts new file mode 100644 index 000000000..75dc6f1f2 --- /dev/null +++ b/packages/order-utils/test/utils/test_order_factory.ts @@ -0,0 +1,32 @@ +import { Order, SignedOrder } from '@0xproject/types'; +import * as _ from 'lodash'; + +import { constants, orderFactory } from '../../src'; + +const BASE_TEST_ORDER: Order = orderFactory.createOrder( + constants.NULL_ADDRESS, + constants.ZERO_AMOUNT, + constants.NULL_ADDRESS, + constants.ZERO_AMOUNT, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, +); +const BASE_TEST_SIGNED_ORDER: SignedOrder = { + ...BASE_TEST_ORDER, + signature: constants.NULL_BYTES, +}; + +export const testOrderFactory = { + generateTestSignedOrder(partialOrder: Partial<SignedOrder>): SignedOrder { + return transformObject(BASE_TEST_SIGNED_ORDER, partialOrder); + }, + generateTestSignedOrders(partialOrder: Partial<SignedOrder>, numOrders: number): SignedOrder[] { + const baseTestOrders = _.map(_.range(numOrders), () => BASE_TEST_SIGNED_ORDER); + return _.map(baseTestOrders, order => transformObject(order, partialOrder)); + }, +}; + +function transformObject<T>(input: T, transformation: Partial<T>): T { + const copy = _.cloneDeep(input); + return _.assign(copy, transformation); +} |