From 32ab4dcac723e189ce8a1be796f0539975a48eae Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 15:52:30 -0400 Subject: Implement rate utils --- packages/order-utils/src/index.ts | 1 + packages/order-utils/src/rate_utils.ts | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 packages/order-utils/src/rate_utils.ts (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 858f500c6..a52f936b6 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -33,3 +33,4 @@ export { EIP712Utils } from './eip712_utils'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { marketUtils } from './market_utils'; +export { rateUtils } from './rate_utils'; diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts new file mode 100644 index 000000000..212431e0c --- /dev/null +++ b/packages/order-utils/src/rate_utils.ts @@ -0,0 +1,44 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +import { assert } from './assert'; +import { constants } from './constants'; + +export const rateUtils = { + /** + * Takes a signed order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset + * is required to cover the fees (feeRate * takerFee), adding the takerAssetAmount and dividing by makerAssetAmount + * @param signedOrder An object that conforms to the signedOrder interface + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * @return The rate (takerAsset/makerAsset) of the order adjusted for fees + */ + getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber): BigNumber { + assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + assert.isBigNumber('feeRate', feeRate); + assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); + const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); + const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); + return rate; + }, + /** + * Takes a signed fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates + * the fee adjusted rate (WETH/ZRX) by dividing the takerAssetAmount by the makerAmount minus the takerFee + * @param signedFeeOrder An object that conforms to the signedOrder interface + * @return The rate (WETH/ZRX) of the fee order adjusted for fees + */ + getFeeAdjustedRateOfFeeOrder(signedFeeOrder: SignedOrder): BigNumber { + assert.doesConformToSchema('signedFeeOrder', signedFeeOrder, schemas.signedOrderSchema); + const zrxAmountAfterFees = signedFeeOrder.makerAssetAmount.sub(signedFeeOrder.takerFee); + assert.assert( + zrxAmountAfterFees.greaterThan(constants.ZERO_AMOUNT), + `Expected takerFee: ${JSON.stringify( + signedFeeOrder.takerFee, + )} to be less than makerAssetAmount: ${JSON.stringify(signedFeeOrder.makerAssetAmount)}`, + ); + const rate = signedFeeOrder.takerAssetAmount.div(zrxAmountAfterFees); + return rate; + }, +}; -- cgit From a1860b076d9d3408e64d8ff5e6bc0657e9ff7e7b Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 17:24:08 -0400 Subject: Add tests for rateUtils --- packages/order-utils/test/rate_utils_test.ts | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 packages/order-utils/test/rate_utils_test.ts (limited to 'packages/order-utils') diff --git a/packages/order-utils/test/rate_utils_test.ts b/packages/order-utils/test/rate_utils_test.ts new file mode 100644 index 000000000..2f321a7db --- /dev/null +++ b/packages/order-utils/test/rate_utils_test.ts @@ -0,0 +1,55 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { constants, rateUtils } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrderFactory } from './utils/test_order_factory'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('rateUtils', () => { + const testOrder = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(100), + takerFee: new BigNumber(20), + }); + describe('#getFeeAdjustedRateOfOrder', () => { + it('throws when feeRate is zero', async () => { + const feeRate = constants.ZERO_AMOUNT; + expect(() => rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate)).to.throw( + 'Expected feeRate: 0 to be greater than 0', + ); + }); + it('throws when feeRate is less than zero', async () => { + const feeRate = new BigNumber(-1); + expect(() => rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate)).to.throw( + 'Expected feeRate: -1 to be greater than 0', + ); + }); + it('correctly calculates fee adjusted rate', async () => { + const feeRate = new BigNumber(2); // ZRX costs 2 units of takerAsset per 1 unit of ZRX + const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate); + // the order actually takes 100 + (2 * 20) takerAsset units to fill 100 units of makerAsset + expect(feeAdjustedRate).to.bignumber.equal(new BigNumber(1.4)); + }); + }); + describe('#getFeeAdjustedRateOfFeeOrder', () => { + it('throws when takerFee exceeds makerAssetAmount', async () => { + const badOrder = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerFee: new BigNumber(101), + }); + expect(() => rateUtils.getFeeAdjustedRateOfFeeOrder(badOrder)).to.throw( + 'Expected takerFee: "101" to be less than makerAssetAmount: "100"', + ); + }); + it('correctly calculates fee adjusted rate', async () => { + const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfFeeOrder(testOrder); + // the order actually takes 100 takerAsset units to fill (100 - 20) units of makerAsset + expect(feeAdjustedRate).to.bignumber.equal(new BigNumber(1.25)); + }); + }); +}); -- cgit From c0924d8067079f246c5cad32f7acb3d1f6cfd9b9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 17:49:22 -0400 Subject: Implement sorting utils --- packages/order-utils/src/index.ts | 1 + packages/order-utils/src/sorting_utils.ts | 69 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 packages/order-utils/src/sorting_utils.ts (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index a52f936b6..359954641 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -34,3 +34,4 @@ export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { marketUtils } from './market_utils'; export { rateUtils } from './rate_utils'; +export { sortingUtils } from './sorting_utils'; diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts new file mode 100644 index 000000000..7474a302f --- /dev/null +++ b/packages/order-utils/src/sorting_utils.ts @@ -0,0 +1,69 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { assert } from './assert'; +import { constants } from './constants'; +import { rateUtils } from './rate_utils'; + +export const sortingUtils = { + /** + * Takes an array of signed orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). + * Adjusts the rate of each order according to the feeRate and takerFee for that order. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * @return The input orders sorted by rate in ascending order + */ + sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('feeRate', feeRate); + assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); + const sortedOrders = sortOrders(signedOrders, rateCalculator); + return sortedOrders; + }, + /** + * Takes an array of signed fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) + * and sorts them by rate in ascending order (best rate first). Adjusts the rate according to the takerFee. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @return The input orders sorted by rate in ascending order + */ + sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { + assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder; + const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); + return sortedOrders; + }, +}; + +type RateCalculator = (signedOrder: SignedOrder) => BigNumber; + +// takes an array of orders, copies them, and sorts the copy based on the rate definition provided by rateCalculator +const sortOrders = (signedOrders: SignedOrder[], rateCalculator: RateCalculator): SignedOrder[] => { + const copiedOrders = _.cloneDeep(signedOrders); + const feeOrderComparator = getOrderComparator(rateCalculator); + copiedOrders.sort(feeOrderComparator); + return copiedOrders; +}; + +type Comparator = (first: T, second: T) => number; + +// takes a function that calculates rate for a signed order and returns a comparator that sorts based on rate +const getOrderComparator = (rateCalculator: RateCalculator): Comparator => ( + firstSignedOrder, + secondSignedOrder, +) => { + const firstOrderRate = rateCalculator(firstSignedOrder); + const secondOrderRate = rateCalculator(secondSignedOrder); + if (firstOrderRate.lt(secondOrderRate)) { + return -1; + } else if (firstOrderRate.gt(secondOrderRate)) { + return 1; + } else { + return 0; + } +}; -- cgit From fcd57d2743e4b6a1363b8071696147a91d2afb00 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 18:43:31 -0400 Subject: Add tests for sortingUtils --- packages/order-utils/src/sorting_utils.ts | 1 - packages/order-utils/test/sorting_utils_test.ts | 66 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 packages/order-utils/test/sorting_utils_test.ts (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 7474a302f..2acd8180f 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -20,7 +20,6 @@ export const sortingUtils = { sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.isBigNumber('feeRate', feeRate); - assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); const sortedOrders = sortOrders(signedOrders, rateCalculator); return sortedOrders; diff --git a/packages/order-utils/test/sorting_utils_test.ts b/packages/order-utils/test/sorting_utils_test.ts new file mode 100644 index 000000000..efce13d3f --- /dev/null +++ b/packages/order-utils/test/sorting_utils_test.ts @@ -0,0 +1,66 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import * as _ from 'lodash'; +import 'mocha'; + +import { constants, rateUtils, sortingUtils } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { testOrderFactory } from './utils/test_order_factory'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('sortingUtils', () => { + describe('#sortOrdersByFeeAdjustedRate', () => { + // rate: 2 takerAsset / makerAsset + const testOrder1 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + }); + // rate: 1 takerAsset / makerAsset + const testOrder2 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(100), + }); + // rate: 2.5 takerAsset / makerAsset + const testOrder3 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + takerFee: new BigNumber(50), + }); + it('correctly sorts by fee adjusted rate', async () => { + const feeRate = new BigNumber(1); // ZRX costs 1 unit of takerAsset per 1 unit of ZRX + const orders = [testOrder1, testOrder2, testOrder3]; + const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate( + [testOrder1, testOrder2, testOrder3], + feeRate, + ); + expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); + }); + }); + describe('#sortFeeOrdersByFeeAdjustedRate', () => { + // rate: 200 takerAsset / makerAsset + const testOrder1 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + takerFee: new BigNumber(99), + }); + // rate: 1 takerAsset / makerAsset + const testOrder2 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(100), + }); + // rate: 4 takerAsset / makerAsset + const testOrder3 = testOrderFactory.generateTestSignedOrder({ + makerAssetAmount: new BigNumber(100), + takerAssetAmount: new BigNumber(200), + takerFee: new BigNumber(50), + }); + it('correctly sorts by fee adjusted rate', async () => { + const orders = [testOrder1, testOrder2, testOrder3]; + const sortedOrders = sortingUtils.sortFeeOrdersByFeeAdjustedRate([testOrder1, testOrder2, testOrder3]); + expect(sortedOrders).to.deep.equal([testOrder2, testOrder3, testOrder1]); + }); + }); +}); -- cgit From cbe639866ec9d6088cc7aa133033d2735524c5a0 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 18:54:17 -0400 Subject: Make feeRate optional with a default of 0 --- packages/order-utils/src/rate_utils.ts | 9 +++++++-- packages/order-utils/src/sorting_utils.ts | 6 +++++- packages/order-utils/test/rate_utils_test.ts | 8 +------- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index 212431e0c..72d11584a 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,6 +1,7 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; @@ -12,12 +13,16 @@ export const rateUtils = { * @param signedOrder An object that conforms to the signedOrder interface * @param feeRate The market rate of ZRX denominated in takerAssetAmount * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The rate (takerAsset/makerAsset) of the order adjusted for fees */ - getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber): BigNumber { + getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('feeRate', feeRate); - assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + assert.assert( + feeRate.gte(constants.ZERO_AMOUNT), + `Expected feeRate: ${feeRate} to be greater than or equal to 0`, + ); const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 2acd8180f..8424060fc 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -15,9 +15,13 @@ export const sortingUtils = { * the makerAsset and WETH as the takerAsset. * @param feeRate The market rate of ZRX denominated in takerAssetAmount * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The input orders sorted by rate in ascending order */ - sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { + sortOrdersByFeeAdjustedRate( + signedOrders: SignedOrder[], + feeRate: BigNumber = constants.ZERO_AMOUNT, + ): SignedOrder[] { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.isBigNumber('feeRate', feeRate); const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); diff --git a/packages/order-utils/test/rate_utils_test.ts b/packages/order-utils/test/rate_utils_test.ts index 2f321a7db..167a40465 100644 --- a/packages/order-utils/test/rate_utils_test.ts +++ b/packages/order-utils/test/rate_utils_test.ts @@ -17,16 +17,10 @@ describe('rateUtils', () => { takerFee: new BigNumber(20), }); describe('#getFeeAdjustedRateOfOrder', () => { - it('throws when feeRate is zero', async () => { - const feeRate = constants.ZERO_AMOUNT; - expect(() => rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate)).to.throw( - 'Expected feeRate: 0 to be greater than 0', - ); - }); it('throws when feeRate is less than zero', async () => { const feeRate = new BigNumber(-1); expect(() => rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate)).to.throw( - 'Expected feeRate: -1 to be greater than 0', + 'Expected feeRate: -1 to be greater than or equal to 0', ); }); it('correctly calculates fee adjusted rate', async () => { -- cgit From af52598d320462357d3ce40d37cf7de9a4f054ee Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 18:59:29 -0400 Subject: Update tests for optional feeRate --- packages/order-utils/test/rate_utils_test.ts | 8 +++++++- packages/order-utils/test/sorting_utils_test.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'packages/order-utils') diff --git a/packages/order-utils/test/rate_utils_test.ts b/packages/order-utils/test/rate_utils_test.ts index 167a40465..b23a6467a 100644 --- a/packages/order-utils/test/rate_utils_test.ts +++ b/packages/order-utils/test/rate_utils_test.ts @@ -23,12 +23,18 @@ describe('rateUtils', () => { 'Expected feeRate: -1 to be greater than or equal to 0', ); }); - it('correctly calculates fee adjusted rate', async () => { + it('correctly calculates fee adjusted rate when feeRate is provided', async () => { const feeRate = new BigNumber(2); // ZRX costs 2 units of takerAsset per 1 unit of ZRX const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfOrder(testOrder, feeRate); // the order actually takes 100 + (2 * 20) takerAsset units to fill 100 units of makerAsset expect(feeAdjustedRate).to.bignumber.equal(new BigNumber(1.4)); }); + it('correctly calculates fee adjusted rate when no feeRate is provided', async () => { + const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfOrder(testOrder); + // because no feeRate was provided we just assume 0 fees + // the order actually takes 100 takerAsset units to fill 100 units of makerAsset + expect(feeAdjustedRate).to.bignumber.equal(new BigNumber(1)); + }); }); describe('#getFeeAdjustedRateOfFeeOrder', () => { it('throws when takerFee exceeds makerAssetAmount', async () => { diff --git a/packages/order-utils/test/sorting_utils_test.ts b/packages/order-utils/test/sorting_utils_test.ts index efce13d3f..5a33cb2ca 100644 --- a/packages/order-utils/test/sorting_utils_test.ts +++ b/packages/order-utils/test/sorting_utils_test.ts @@ -13,6 +13,7 @@ const expect = chai.expect; describe('sortingUtils', () => { describe('#sortOrdersByFeeAdjustedRate', () => { + const feeRate = new BigNumber(1); // ZRX costs 1 unit of takerAsset per 1 unit of ZRX // rate: 2 takerAsset / makerAsset const testOrder1 = testOrderFactory.generateTestSignedOrder({ makerAssetAmount: new BigNumber(100), @@ -29,8 +30,7 @@ describe('sortingUtils', () => { takerAssetAmount: new BigNumber(200), takerFee: new BigNumber(50), }); - it('correctly sorts by fee adjusted rate', async () => { - const feeRate = new BigNumber(1); // ZRX costs 1 unit of takerAsset per 1 unit of ZRX + it('correctly sorts by fee adjusted rate when feeRate is Provided', async () => { const orders = [testOrder1, testOrder2, testOrder3]; const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate( [testOrder1, testOrder2, testOrder3], @@ -38,6 +38,11 @@ describe('sortingUtils', () => { ); expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); }); + it('correctly sorts by fee adjusted rate when no feeRate is Provided', async () => { + const orders = [testOrder1, testOrder2, testOrder3]; + const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate([testOrder1, testOrder2, testOrder3]); + expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); + }); }); describe('#sortFeeOrdersByFeeAdjustedRate', () => { // rate: 200 takerAsset / makerAsset -- cgit From b86210332f94ad90cdabcf2083ae4a99075daf33 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 19:10:57 -0400 Subject: Fix lint errors --- packages/order-utils/src/rate_utils.ts | 1 - packages/order-utils/src/sorting_utils.ts | 2 +- packages/order-utils/test/rate_utils_test.ts | 2 +- packages/order-utils/test/sorting_utils_test.ts | 12 ++++-------- 4 files changed, 6 insertions(+), 11 deletions(-) (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index 72d11584a..e82c03873 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,7 +1,6 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 8424060fc..f019aa4a8 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -37,7 +37,7 @@ export const sortingUtils = { */ sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder; + const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils); const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); return sortedOrders; }, diff --git a/packages/order-utils/test/rate_utils_test.ts b/packages/order-utils/test/rate_utils_test.ts index b23a6467a..2e299c209 100644 --- a/packages/order-utils/test/rate_utils_test.ts +++ b/packages/order-utils/test/rate_utils_test.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import 'mocha'; -import { constants, rateUtils } from '../src'; +import { rateUtils } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { testOrderFactory } from './utils/test_order_factory'; diff --git a/packages/order-utils/test/sorting_utils_test.ts b/packages/order-utils/test/sorting_utils_test.ts index 5a33cb2ca..016432505 100644 --- a/packages/order-utils/test/sorting_utils_test.ts +++ b/packages/order-utils/test/sorting_utils_test.ts @@ -1,9 +1,8 @@ import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; -import * as _ from 'lodash'; import 'mocha'; -import { constants, rateUtils, sortingUtils } from '../src'; +import { sortingUtils } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { testOrderFactory } from './utils/test_order_factory'; @@ -32,15 +31,12 @@ describe('sortingUtils', () => { }); it('correctly sorts by fee adjusted rate when feeRate is Provided', async () => { const orders = [testOrder1, testOrder2, testOrder3]; - const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate( - [testOrder1, testOrder2, testOrder3], - feeRate, - ); + const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate(orders, feeRate); expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); }); it('correctly sorts by fee adjusted rate when no feeRate is Provided', async () => { const orders = [testOrder1, testOrder2, testOrder3]; - const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate([testOrder1, testOrder2, testOrder3]); + const sortedOrders = sortingUtils.sortOrdersByFeeAdjustedRate(orders); expect(sortedOrders).to.deep.equal([testOrder2, testOrder1, testOrder3]); }); }); @@ -64,7 +60,7 @@ describe('sortingUtils', () => { }); it('correctly sorts by fee adjusted rate', async () => { const orders = [testOrder1, testOrder2, testOrder3]; - const sortedOrders = sortingUtils.sortFeeOrdersByFeeAdjustedRate([testOrder1, testOrder2, testOrder3]); + const sortedOrders = sortingUtils.sortFeeOrdersByFeeAdjustedRate(orders); expect(sortedOrders).to.deep.equal([testOrder2, testOrder3, testOrder1]); }); }); -- cgit From ab64ea7377b4cd0a0a7e3ec219f958037c5fabbe Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 19:20:07 -0400 Subject: Updated CHANGELOG --- packages/order-utils/CHANGELOG.json | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'packages/order-utils') diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index fa82976ad..ee0030b6b 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -13,6 +13,10 @@ }, { "note": "Dependencies updated" + }, + { + "note": "Added rateUtils and sortingUtils", + "pr": 953 } ] }, -- cgit From d8593998414aa3234533b8fa868b05495ac54457 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Aug 2018 17:45:50 -0700 Subject: Change rateUtils to use Order --- packages/order-utils/src/rate_utils.ts | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index e82c03873..c9ca72c59 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { assert } from './assert'; @@ -7,42 +7,42 @@ import { constants } from './constants'; export const rateUtils = { /** - * Takes a signed order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset + * Takes an order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset * is required to cover the fees (feeRate * takerFee), adding the takerAssetAmount and dividing by makerAssetAmount - * @param signedOrder An object that conforms to the signedOrder interface - * @param feeRate The market rate of ZRX denominated in takerAssetAmount - * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) - * Defaults to 0 + * @param order An object that conforms to the order interface + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The rate (takerAsset/makerAsset) of the order adjusted for fees */ - getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + getFeeAdjustedRateOfOrder(order: Order, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { + assert.doesConformToSchema('order', order, schemas.orderSchema); assert.isBigNumber('feeRate', feeRate); assert.assert( feeRate.gte(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than or equal to 0`, ); - const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); - const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); - const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); + const takerAssetAmountNeededToPayForFees = order.takerFee.mul(feeRate); + const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(order.takerAssetAmount); + const rate = totalTakerAssetAmount.div(order.makerAssetAmount); return rate; }, /** - * Takes a signed fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates + * Takes a fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates * the fee adjusted rate (WETH/ZRX) by dividing the takerAssetAmount by the makerAmount minus the takerFee - * @param signedFeeOrder An object that conforms to the signedOrder interface + * @param feeOrder An object that conforms to the order interface * @return The rate (WETH/ZRX) of the fee order adjusted for fees */ - getFeeAdjustedRateOfFeeOrder(signedFeeOrder: SignedOrder): BigNumber { - assert.doesConformToSchema('signedFeeOrder', signedFeeOrder, schemas.signedOrderSchema); - const zrxAmountAfterFees = signedFeeOrder.makerAssetAmount.sub(signedFeeOrder.takerFee); + getFeeAdjustedRateOfFeeOrder(feeOrder: Order): BigNumber { + assert.doesConformToSchema('feeOrder', feeOrder, schemas.orderSchema); + const zrxAmountAfterFees = feeOrder.makerAssetAmount.sub(feeOrder.takerFee); assert.assert( zrxAmountAfterFees.greaterThan(constants.ZERO_AMOUNT), - `Expected takerFee: ${JSON.stringify( - signedFeeOrder.takerFee, - )} to be less than makerAssetAmount: ${JSON.stringify(signedFeeOrder.makerAssetAmount)}`, + `Expected takerFee: ${JSON.stringify(feeOrder.takerFee)} to be less than makerAssetAmount: ${JSON.stringify( + feeOrder.makerAssetAmount, + )}`, ); - const rate = signedFeeOrder.takerAssetAmount.div(zrxAmountAfterFees); + const rate = feeOrder.takerAssetAmount.div(zrxAmountAfterFees); return rate; }, }; -- cgit From 99b744ba52f538752bb0966e6d8b50d9f5a2f032 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Aug 2018 18:31:41 -0700 Subject: Update sortingUtils to support Order and SignedOrder --- packages/order-utils/src/sorting_utils.ts | 70 ++++++++++++------------------- 1 file changed, 26 insertions(+), 44 deletions(-) (limited to 'packages/order-utils') diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index f019aa4a8..8811bcaf8 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -9,64 +9,46 @@ import { rateUtils } from './rate_utils'; export const sortingUtils = { /** - * Takes an array of signed orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). + * Takes an array of orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). * Adjusts the rate of each order according to the feeRate and takerFee for that order. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param feeRate The market rate of ZRX denominated in takerAssetAmount - * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) - * Defaults to 0 + * @param orders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The input orders sorted by rate in ascending order */ - sortOrdersByFeeAdjustedRate( - signedOrders: SignedOrder[], - feeRate: BigNumber = constants.ZERO_AMOUNT, - ): SignedOrder[] { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + sortOrdersByFeeAdjustedRate(orders: T[], feeRate: BigNumber = constants.ZERO_AMOUNT): T[] { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.isBigNumber('feeRate', feeRate); - const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); - const sortedOrders = sortOrders(signedOrders, rateCalculator); + const rateCalculator = (order: Order) => rateUtils.getFeeAdjustedRateOfOrder(order, feeRate); + const sortedOrders = sortOrders(orders, rateCalculator); return sortedOrders; }, /** - * Takes an array of signed fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) + * Takes an array of fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) * and sorts them by rate in ascending order (best rate first). Adjusts the rate according to the takerFee. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. + * @param feeOrders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. * @return The input orders sorted by rate in ascending order */ - sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { - assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + sortFeeOrdersByFeeAdjustedRate(feeOrders: Order[]): Order[] { + assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils); - const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); + const sortedOrders = sortOrders(feeOrders, rateCalculator); return sortedOrders; }, }; -type RateCalculator = (signedOrder: SignedOrder) => BigNumber; +type RateCalculator = (order: Order) => BigNumber; // takes an array of orders, copies them, and sorts the copy based on the rate definition provided by rateCalculator -const sortOrders = (signedOrders: SignedOrder[], rateCalculator: RateCalculator): SignedOrder[] => { - const copiedOrders = _.cloneDeep(signedOrders); - const feeOrderComparator = getOrderComparator(rateCalculator); - copiedOrders.sort(feeOrderComparator); +function sortOrders(orders: T[], rateCalculator: RateCalculator): T[] { + const copiedOrders = _.cloneDeep(orders); + copiedOrders.sort((firstOrder, secondOrder) => { + const firstOrderRate = rateCalculator(firstOrder); + const secondOrderRate = rateCalculator(secondOrder); + return firstOrderRate.comparedTo(secondOrderRate); + }); return copiedOrders; -}; - -type Comparator = (first: T, second: T) => number; - -// takes a function that calculates rate for a signed order and returns a comparator that sorts based on rate -const getOrderComparator = (rateCalculator: RateCalculator): Comparator => ( - firstSignedOrder, - secondSignedOrder, -) => { - const firstOrderRate = rateCalculator(firstSignedOrder); - const secondOrderRate = rateCalculator(secondSignedOrder); - if (firstOrderRate.lt(secondOrderRate)) { - return -1; - } else if (firstOrderRate.gt(secondOrderRate)) { - return 1; - } else { - return 0; - } -}; +} -- cgit