protocol/contracts/exchange-libs/test/reference_functions.ts
2019-11-27 11:52:14 +11:00

329 lines
16 KiB
TypeScript

import { constants, describe, expect } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { BigNumber, LibMathRevertErrors } 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),
protocolFeePaid: ONE_ETHER.times(0.003),
},
{
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),
protocolFeePaid: ONE_ETHER.times(0.003).times(0.01),
},
];
it('reverts if computing `makerAssetFilledAmount` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.makerAssetFilledAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerFeePaid,
b.takerFeePaid,
);
expect(() => addFillResults(a, b)).to.throw(expectedError.message);
});
it('reverts if computing `protocolFeePaid` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.protocolFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.protocolFeePaid,
b.protocolFeePaid,
);
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
return expect(() => safeGetPartialAmountCeil(numerator, denominator, target)).to.throw(
expectedError.message,
);
});
});
});
describe('isRoundingErrorFloor', () => {
describe('explicit tests', () => {
it('returns true when `numerator * target / denominator` produces an error >= 0.1%', async () => {
const numerator = new BigNumber(100);
const denominator = new BigNumber(102);
const target = new BigNumber(52);
// tslint:disable-next-line: boolean-naming
const actual = isRoundingErrorFloor(numerator, denominator, target);
expect(actual).to.eq(true);
});
it('returns false when `numerator * target / denominator` produces an error < 0.1%', async () => {
const numerator = new BigNumber(100);
const denominator = new BigNumber(101);
const target = new BigNumber(92);
// tslint:disable-next-line: boolean-naming
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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
return expect(() => isRoundingErrorFloor(numerator, denominator, target)).to.throw(
expectedError.message,
);
});
});
});
describe('isRoundingErrorCeil', () => {
describe('explicit tests', () => {
it('returns true when `numerator * target / (denominator - 1)` produces an error >= 0.1%', async () => {
const numerator = new BigNumber(100);
const denominator = new BigNumber(101);
const target = new BigNumber(92);
// tslint:disable-next-line: boolean-naming
const actual = isRoundingErrorCeil(numerator, denominator, target);
expect(actual).to.eq(true);
});
it('returns false when `numerator * target / (denominator - 1)` produces an error < 0.1%', async () => {
const numerator = new BigNumber(100);
const denominator = new BigNumber(102);
const target = new BigNumber(52);
// tslint:disable-next-line: boolean-naming
const actual = isRoundingErrorCeil(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.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
return expect(() => isRoundingErrorCeil(numerator, denominator, target)).to.throw(
expectedError.message,
);
});
});
});
});
});