From 293510c087cc0642a908ce16ec3d526ea2c3a6a3 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Fri, 2 Aug 2019 13:02:16 -0400 Subject: [PATCH] `@0x/contracts-exchange-libs`: Add explicit tests for `LibMath` and `LibFillResults` functions. `@0x/contracts-exchange-libs`: Add tests for `ReferenceFunctions`. --- .../exchange-libs/test/lib_fill_results.ts | 114 ++++--- contracts/exchange-libs/test/lib_math.ts | 271 ++++++++++++++- .../exchange-libs/test/reference_functions.ts | 310 ++++++++++++++++++ 3 files changed, 652 insertions(+), 43 deletions(-) create mode 100644 contracts/exchange-libs/test/reference_functions.ts diff --git a/contracts/exchange-libs/test/lib_fill_results.ts b/contracts/exchange-libs/test/lib_fill_results.ts index d4c7f4b94c..ebfd3a4957 100644 --- a/contracts/exchange-libs/test/lib_fill_results.ts +++ b/contracts/exchange-libs/test/lib_fill_results.ts @@ -1,16 +1,17 @@ import { blockchainTests, + constants, describe, - testCombinatoriallyWithReferenceFunc, - uint256Values, + expect, } from '@0x/contracts-test-utils'; -import { FillResults } from '@0x/types'; -import { BigNumber } from '@0x/utils'; +import { BigNumber, SafeMathRevertErrors } from '@0x/utils'; +import * as _ from 'lodash'; import { artifacts, ReferenceFunctions, TestLibsContract } from '../src'; blockchainTests('LibFillResults', env => { const CHAIN_ID = 1337; + const { ONE_ETHER, MAX_UINT256 } = constants; let libsContract: TestLibsContract; before(async () => { @@ -23,47 +24,76 @@ blockchainTests('LibFillResults', env => { }); describe('addFillResults', () => { - function makeFillResults(value: BigNumber): FillResults { - // HACK(dorothy-zbornak): We reuse values across fields, - // but this is fine because `addFillResults()` never does - // any math between them. - return { - makerAssetFilledAmount: value, - takerAssetFilledAmount: value, - makerFeePaid: value, - takerFeePaid: value, - }; - } + describe('explicit tests', () => { + const DEFAULT_FILL_RESULTS = [ + { + makerAssetFilledAmount: ONE_ETHER, + takerAssetFilledAmount: ONE_ETHER.times(2), + makerFeePaid: ONE_ETHER.times(0.001), + takerFeePaid: ONE_ETHER.times(0.002), + }, + { + makerAssetFilledAmount: ONE_ETHER.times(0.01), + takerAssetFilledAmount: ONE_ETHER.times(2).times(0.01), + makerFeePaid: ONE_ETHER.times(0.001).times(0.01), + takerFeePaid: ONE_ETHER.times(0.002).times(0.01), + }, + ]; - async function referenceAddFillResultsAsync( - totalValue: BigNumber, - singleValue: BigNumber, - ): Promise { - return ReferenceFunctions.addFillResults( - makeFillResults(totalValue), - makeFillResults(singleValue), - ); - } + it('matches the output of the reference function', async () => { + const [ a, b ] = DEFAULT_FILL_RESULTS; + const expected = ReferenceFunctions.addFillResults(a, b); + const actual = await libsContract.addFillResults.callAsync(a, b); + expect(actual).to.deep.equal(expected); + }); - async function testAddFillResultsAsync( - totalValue: BigNumber, - singleValue: BigNumber, - ): Promise { - return libsContract.addFillResults.callAsync( - makeFillResults(totalValue), - makeFillResults(singleValue), - ); - } + it('reverts if computing `makerAssetFilledAmount` overflows', async () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.makerAssetFilledAmount = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.makerAssetFilledAmount, + b.makerAssetFilledAmount, + ); + return expect(libsContract.addFillResults.callAsync(a, b)) + .to.revertWith(expectedError); + }); - // TODO(dorothy-zbornak): Do we really need these? - // Just a couple edge cases would likely suffice. - describe.optional('combinatorial tests', () => { - testCombinatoriallyWithReferenceFunc( - 'addFillResults', - referenceAddFillResultsAsync, - testAddFillResultsAsync, - [uint256Values, uint256Values], - ); + it('reverts if computing `takerAssetFilledAmount` overflows', async () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.takerAssetFilledAmount = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.takerAssetFilledAmount, + b.takerAssetFilledAmount, + ); + return expect(libsContract.addFillResults.callAsync(a, b)) + .to.revertWith(expectedError); + }); + + it('reverts if computing `makerFeePaid` overflows', async () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.makerFeePaid = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.makerFeePaid, + b.makerFeePaid, + ); + return expect(libsContract.addFillResults.callAsync(a, b)) + .to.revertWith(expectedError); + }); + + it('reverts if computing `takerFeePaid` overflows', async () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.takerFeePaid = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.takerFeePaid, + b.takerFeePaid, + ); + return expect(libsContract.addFillResults.callAsync(a, b)) + .to.revertWith(expectedError); + }); }); }); }); diff --git a/contracts/exchange-libs/test/lib_math.ts b/contracts/exchange-libs/test/lib_math.ts index b6db862909..9215a1e747 100644 --- a/contracts/exchange-libs/test/lib_math.ts +++ b/contracts/exchange-libs/test/lib_math.ts @@ -1,15 +1,19 @@ import { blockchainTests, + constants, describe, + expect, testCombinatoriallyWithReferenceFunc, uint256Values, } from '@0x/contracts-test-utils'; -import { BigNumber } from '@0x/utils'; +import { LibMathRevertErrors } from '@0x/order-utils'; +import { BigNumber, SafeMathRevertErrors } from '@0x/utils'; import { artifacts, ReferenceFunctions, TestLibsContract } from '../src'; blockchainTests('LibMath', env => { const CHAIN_ID = 1337; + const { ONE_ETHER, MAX_UINT256, MAX_UINT256_ROOT, ZERO_AMOUNT } = constants; let libsContract: TestLibsContract; before(async () => { @@ -48,6 +52,43 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.getPartialAmountFloor(numerator, denominator, target); + const actual = await libsContract.getPartialAmountFloor.callAsync(numerator, denominator, target); + expect(actual).to.bignumber.eq(expected); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero, + numerator.times(target), + denominator, + ); + return expect(libsContract.getPartialAmountFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.getPartialAmountFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); describe('getPartialAmountCeil', () => { @@ -59,6 +100,44 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.getPartialAmountCeil(numerator, denominator, target); + const actual = await libsContract.getPartialAmountCeil.callAsync(numerator, denominator, target); + expect(actual).to.bignumber.eq(expected); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + // This will actually manifest as a subtraction underflow. + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow, + denominator, + new BigNumber(1), + ); + return expect(libsContract.getPartialAmountCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.getPartialAmountCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); describe('safeGetPartialAmountFloor', () => { @@ -70,6 +149,52 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.safeGetPartialAmountFloor(numerator, denominator, target); + const actual = await libsContract.safeGetPartialAmountFloor.callAsync(numerator, denominator, target); + expect(actual).to.bignumber.eq(expected); + }); + + it('reverts for a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const expectedError = new LibMathRevertErrors.RoundingError( + numerator, + denominator, + target, + ); + return expect(libsContract.safeGetPartialAmountFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(libsContract.safeGetPartialAmountFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.safeGetPartialAmountFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); describe('safeGetPartialAmountCeil', () => { @@ -81,6 +206,52 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.safeGetPartialAmountCeil(numerator, denominator, target); + const actual = await libsContract.safeGetPartialAmountCeil.callAsync(numerator, denominator, target); + expect(actual).to.bignumber.eq(expected); + }); + + it('reverts for a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const expectedError = new LibMathRevertErrors.RoundingError( + numerator, + denominator, + target, + ); + return expect(libsContract.safeGetPartialAmountCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(libsContract.safeGetPartialAmountCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.safeGetPartialAmountCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); describe('isRoundingErrorFloor', () => { @@ -92,6 +263,55 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('returns true for a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const actual = await libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target); + expect(actual).to.eq(true); + }); + + it('returns false for not a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(5e2); + const actual = await libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target); + expect(actual).to.eq(false); + }); + + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.isRoundingErrorFloor(numerator, denominator, target); + const actual = await libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target); + expect(actual).to.eq(expected); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); describe('isRoundingErrorCeil', () => { @@ -103,5 +323,54 @@ blockchainTests('LibMath', env => { [uint256Values, uint256Values, uint256Values], ); }); + + describe('explicit tests', () => { + it('returns true for a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const actual = await libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target); + expect(actual).to.eq(true); + }); + + it('returns false for not a rounding error', async () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(5e2); + const actual = await libsContract.isRoundingErrorFloor.callAsync(numerator, denominator, target); + expect(actual).to.eq(false); + }); + + it('matches the reference function output', async () => { + const numerator = ONE_ETHER; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = ONE_ETHER.times(0.01); + const expected = ReferenceFunctions.isRoundingErrorCeil(numerator, denominator, target); + const actual = await libsContract.isRoundingErrorCeil.callAsync(numerator, denominator, target); + expect(actual).to.eq(expected); + }); + + it('reverts if `denominator` is zero', async () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(libsContract.isRoundingErrorCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + + it('reverts if `numerator * target` overflows', async () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(libsContract.isRoundingErrorCeil.callAsync(numerator, denominator, target)) + .to.revertWith(expectedError); + }); + }); }); }); diff --git a/contracts/exchange-libs/test/reference_functions.ts b/contracts/exchange-libs/test/reference_functions.ts new file mode 100644 index 0000000000..c7ea2536e2 --- /dev/null +++ b/contracts/exchange-libs/test/reference_functions.ts @@ -0,0 +1,310 @@ +import { + constants, + describe, + expect, +} from '@0x/contracts-test-utils'; +import { LibMathRevertErrors } from '@0x/order-utils'; +import { BigNumber, SafeMathRevertErrors } from '@0x/utils'; +import * as _ from 'lodash'; + +import { + addFillResults, + getPartialAmountCeil, + getPartialAmountFloor, + isRoundingErrorCeil, + isRoundingErrorFloor, + safeGetPartialAmountCeil, + safeGetPartialAmountFloor, +} from '../src/reference_functions'; + +describe('Reference Functions', () => { + const { ONE_ETHER, MAX_UINT256, MAX_UINT256_ROOT, ZERO_AMOUNT } = constants; + describe('LibFillResults', () => { + + describe('addFillResults', () => { + const DEFAULT_FILL_RESULTS = [ + { + makerAssetFilledAmount: ONE_ETHER, + takerAssetFilledAmount: ONE_ETHER.times(2), + makerFeePaid: ONE_ETHER.times(0.001), + takerFeePaid: ONE_ETHER.times(0.002), + }, + { + makerAssetFilledAmount: ONE_ETHER.times(0.01), + takerAssetFilledAmount: ONE_ETHER.times(2).times(0.01), + makerFeePaid: ONE_ETHER.times(0.001).times(0.01), + takerFeePaid: ONE_ETHER.times(0.002).times(0.01), + }, + ]; + + it('reverts if computing `makerAssetFilledAmount` overflows', () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.makerAssetFilledAmount = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.makerAssetFilledAmount, + b.makerAssetFilledAmount, + ); + expect(() => addFillResults(a, b)).to.throw(expectedError.message); + }); + + it('reverts if computing `takerAssetFilledAmount` overflows', () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.takerAssetFilledAmount = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.takerAssetFilledAmount, + b.takerAssetFilledAmount, + ); + expect(() => addFillResults(a, b)).to.throw(expectedError.message); + }); + + it('reverts if computing `makerFeePaid` overflows', () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.makerFeePaid = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.makerFeePaid, + b.makerFeePaid, + ); + expect(() => addFillResults(a, b)).to.throw(expectedError.message); + }); + + it('reverts if computing `takerFeePaid` overflows', () => { + const [ a, b ] = _.cloneDeep(DEFAULT_FILL_RESULTS); + b.takerFeePaid = MAX_UINT256; + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow, + a.takerFeePaid, + b.takerFeePaid, + ); + expect(() => addFillResults(a, b)).to.throw(expectedError.message); + }); + }); + }); + + describe('LibMath', () => { + describe('getPartialAmountFloor', () => { + describe('explicit tests', () => { + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero, + numerator.times(target), + denominator, + ); + return expect(() => getPartialAmountFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => getPartialAmountFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + + describe('getPartialAmountCeil', () => { + describe('explicit tests', () => { + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + // This will actually manifest as a subtraction underflow. + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow, + denominator, + new BigNumber(1), + ); + return expect(() => getPartialAmountCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => getPartialAmountCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + + describe('safeGetPartialAmountFloor', () => { + describe('explicit tests', () => { + it('reverts for a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const expectedError = new LibMathRevertErrors.RoundingError( + numerator, + denominator, + target, + ); + return expect(() => safeGetPartialAmountFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(() => safeGetPartialAmountFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => safeGetPartialAmountFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + + describe('safeGetPartialAmountCeil', () => { + describe('explicit tests', () => { + it('reverts for a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const expectedError = new LibMathRevertErrors.RoundingError( + numerator, + denominator, + target, + ); + return expect(() => safeGetPartialAmountCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(() => safeGetPartialAmountCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => safeGetPartialAmountCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + + describe('isRoundingErrorFloor', () => { + describe('explicit tests', () => { + it('returns true for a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const actual = isRoundingErrorFloor(numerator, denominator, target); + expect(actual).to.eq(true); + }); + + it('returns false for not a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(5e2); + const actual = isRoundingErrorFloor(numerator, denominator, target); + expect(actual).to.eq(false); + }); + + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(() => isRoundingErrorFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => isRoundingErrorFloor(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + + describe('isRoundingErrorCeil', () => { + describe('explicit tests', () => { + it('returns true for a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(333); + const actual = isRoundingErrorFloor(numerator, denominator, target); + expect(actual).to.eq(true); + }); + + it('returns false for not a rounding error', () => { + const numerator = new BigNumber(1e3); + const denominator = new BigNumber(1e4); + const target = new BigNumber(5e2); + const actual = isRoundingErrorFloor(numerator, denominator, target); + expect(actual).to.eq(false); + }); + + it('reverts if `denominator` is zero', () => { + const numerator = ONE_ETHER; + const denominator = ZERO_AMOUNT; + const target = ONE_ETHER.times(0.01); + const expectedError = new LibMathRevertErrors.DivisionByZeroError(); + return expect(() => isRoundingErrorCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + + it('reverts if `numerator * target` overflows', () => { + const numerator = MAX_UINT256; + const denominator = ONE_ETHER.dividedToIntegerBy(2); + const target = MAX_UINT256_ROOT.times(2); + const expectedError = new SafeMathRevertErrors.SafeMathError( + SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow, + numerator, + target, + ); + return expect(() => isRoundingErrorCeil(numerator, denominator, target)) + .to.throw(expectedError.message); + }); + }); + }); + }); +});