diff options
-rw-r--r-- | packages/order-utils/src/constants.ts | 2 | ||||
-rw-r--r-- | packages/order-utils/src/market_utils.ts | 14 | ||||
-rw-r--r-- | packages/order-utils/test/market_utils_test.ts | 162 | ||||
-rw-r--r-- | packages/order-utils/test/utils/test_order_factory.ts | 7 |
4 files changed, 165 insertions, 20 deletions
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index 5137ff499..c23578c20 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -11,6 +11,6 @@ export const constants = { ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: 53, SELECTOR_LENGTH: 4, BASE_16: 16, - INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite, + INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite ZERO_AMOUNT: new BigNumber(0), }; diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index d66448a0b..94b5be4eb 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -17,7 +17,7 @@ export const marketUtils = { * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups * for these values. * @param makerAssetFillAmount The amount of makerAsset desired to be filled. - * @param slippageBufferAmount An additional amount makerAsset to be covered by the result in case of trade collisions or partial fills. + * @param slippageBufferAmount An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills. * @return Resulting orders and remaining fill amount that could not be covered by the input. */ findOrdersThatCoverMakerAssetFillAmount( @@ -80,8 +80,8 @@ export const marketUtils = { * @param remainingFillableFeeAmounts An array of BigNumbers corresponding to the signedFeeOrders parameter. * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups * for these values. - * @param slippageBufferAmount An additional amount makerAsset to be covered by the result in case of trade collisions or partial fills. - * @return Resulting orders and remaining fill amount that could not be covered by the input. + * @param slippageBufferAmount An additional amount of fee to be covered by the result in case of trade collisions or partial fills. + * @return Resulting orders and remaining fee amount that could not be covered by the input. */ findFeeOrdersThatCoverFeesForTargetOrders( signedOrders: SignedOrder[], @@ -89,7 +89,7 @@ export const marketUtils = { signedFeeOrders: SignedOrder[], remainingFillableFeeAmounts: BigNumber[], slippageBufferAmount: BigNumber = constants.ZERO_AMOUNT, - ): { resultOrders: SignedOrder[]; remainingFillAmount: BigNumber } { + ): { resultOrders: SignedOrder[]; remainingFeeAmount: BigNumber } { // type assertions assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); _.forEach(remainingFillableMakerAssetAmounts, (amount, index) => @@ -121,12 +121,16 @@ export const marketUtils = { }, constants.ZERO_AMOUNT, ); - return marketUtils.findOrdersThatCoverMakerAssetFillAmount( + const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( signedFeeOrders, remainingFillableFeeAmounts, totalFeeAmount, slippageBufferAmount, ); + return { + resultOrders, + remainingFeeAmount: remainingFillAmount, + }; // TODO: add more orders here to cover rounding // https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx }, diff --git a/packages/order-utils/test/market_utils_test.ts b/packages/order-utils/test/market_utils_test.ts index ac3fb9b93..03f86c581 100644 --- a/packages/order-utils/test/market_utils_test.ts +++ b/packages/order-utils/test/market_utils_test.ts @@ -12,7 +12,7 @@ const expect = chai.expect; // tslint:disable: no-unused-expression describe('marketUtils', () => { - describe.only('#findOrdersThatCoverMakerAssetFillAmount', () => { + describe('#findOrdersThatCoverMakerAssetFillAmount', () => { describe('no orders', () => { it('returns empty and unchanged remainingFillAmount', async () => { const fillAmount = new BigNumber(10); @@ -25,15 +25,14 @@ describe('marketUtils', () => { expect(remainingFillAmount).to.be.bignumber.equal(fillAmount); }); }); - describe('orders are all completely fillable', () => { + describe('orders are completely fillable', () => { // generate three signed orders each with 10 units of makerAsset, 30 total - const testOrderCount = 3; const makerAssetAmount = new BigNumber(10); const inputOrders = testOrderFactory.generateTestSignedOrders( { makerAssetAmount, }, - testOrderCount, + 3, ); // generate remainingFillableMakerAssetAmounts that equal the makerAssetAmount const remainingFillableMakerAssetAmounts = [makerAssetAmount, makerAssetAmount, makerAssetAmount]; @@ -95,20 +94,19 @@ describe('marketUtils', () => { }); describe('orders are partially fillable', () => { // generate three signed orders each with 10 units of makerAsset, 30 total - const testOrderCount = 3; const makerAssetAmount = new BigNumber(10); const inputOrders = testOrderFactory.generateTestSignedOrders( { makerAssetAmount, }, - testOrderCount, + 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 2 orders and non-zero remainingFillAmount when trying to fill original makerAssetAmounts', async () => { + 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( @@ -121,5 +119,153 @@ describe('marketUtils', () => { }); }); }); - describe('#findFeeOrdersThatCoverFeesForTargetOrders', () => {}); + 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 index 611e777ea..75dc6f1f2 100644 --- a/packages/order-utils/test/utils/test_order_factory.ts +++ b/packages/order-utils/test/utils/test_order_factory.ts @@ -5,14 +5,9 @@ import { constants, orderFactory } from '../../src'; const BASE_TEST_ORDER: Order = orderFactory.createOrder( constants.NULL_ADDRESS, - constants.NULL_ADDRESS, - constants.NULL_ADDRESS, - constants.ZERO_AMOUNT, constants.ZERO_AMOUNT, + constants.NULL_ADDRESS, constants.ZERO_AMOUNT, - constants.NULL_BYTES, - constants.ZERO_AMOUNT, - constants.NULL_BYTES, constants.NULL_ADDRESS, constants.NULL_ADDRESS, ); |