* update abi-gen with new method interfaces * wip: get all packages to build * wip: get all packages to build * Fix two contract wrapper calls * Export necessary types part of the contract wrapper public interfaces * Revive and fix wrapper_unit_tests * Remove duplicate type * Fix lib_exchange_rich_error_decoder tests * Fix remaining test failures in contracts-* packages * Prettier fixes * remove transactionHelper * lint and update changelogs * Fix prettier * Revert changes to reference docs * Add back changelog already published and add revert changelog entry * Add missing CHANGELOG entries * Add missing comma * Update mesh-rpc-client dep * Update Mesh RPC logic in @0x/orderbook to v6.0.1-beta * Align package versions
955 lines
38 KiB
TypeScript
955 lines
38 KiB
TypeScript
import { blockchainTests, expect, hexRandom, Numberish } from '@0x/contracts-test-utils';
|
|
import { BigNumber, FixedMathRevertErrors } from '@0x/utils';
|
|
import { Decimal } from 'decimal.js';
|
|
import * as _ from 'lodash';
|
|
|
|
import { assertRoughlyEquals, fromFixed, toDecimal, toFixed } from '../utils/number_utils';
|
|
|
|
import { artifacts } from '../artifacts';
|
|
import { TestLibFixedMathContract } from '../wrappers';
|
|
|
|
blockchainTests('LibFixedMath unit tests', env => {
|
|
let testContract: TestLibFixedMathContract;
|
|
|
|
before(async () => {
|
|
testContract = await TestLibFixedMathContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestLibFixedMath,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
});
|
|
|
|
const BITS_OF_PRECISION = 127;
|
|
const FIXED_POINT_DIVISOR = new BigNumber(2).pow(BITS_OF_PRECISION);
|
|
const FIXED_1 = FIXED_POINT_DIVISOR;
|
|
const MAX_FIXED_VALUE = new BigNumber(2).pow(255).minus(1);
|
|
const MIN_FIXED_VALUE = new BigNumber(2).pow(255).times(-1);
|
|
const MIN_EXP_NUMBER = new BigNumber('-63.875');
|
|
const MAX_EXP_NUMBER = new BigNumber(0);
|
|
// e ^ MIN_EXP_NUMBER
|
|
const MIN_LN_NUMBER = new BigNumber(new Decimal(MIN_EXP_NUMBER.toFixed(128)).exp().toFixed(128));
|
|
const FUZZ_COUNT = 1024;
|
|
|
|
function assertFixedEquals(actualFixed: Numberish, expected: Numberish): void {
|
|
expect(fromFixed(actualFixed)).to.bignumber.eq(fromFixed(toFixed(expected)));
|
|
}
|
|
|
|
function assertFixedRoughlyEquals(actualFixed: Numberish, expected: Numberish, precision: number = 18): void {
|
|
assertRoughlyEquals(fromFixed(actualFixed), expected, precision);
|
|
}
|
|
|
|
describe('one()', () => {
|
|
it('equals 1', async () => {
|
|
const r = await testContract.one().callAsync();
|
|
assertFixedEquals(r, 1);
|
|
});
|
|
});
|
|
|
|
describe('abs()', () => {
|
|
it('abs(n) == n', async () => {
|
|
const n = 1337.5912;
|
|
const r = await testContract.abs(toFixed(n)).callAsync();
|
|
assertFixedEquals(r, n);
|
|
});
|
|
|
|
it('abs(-n) == n', async () => {
|
|
const n = -1337.5912;
|
|
const r = await testContract.abs(toFixed(n)).callAsync();
|
|
assertFixedEquals(r, -n);
|
|
});
|
|
|
|
it('abs(0) == 0', async () => {
|
|
const n = 0;
|
|
const r = await testContract.abs(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('abs(MAX_FIXED) == MAX_FIXED', async () => {
|
|
const n = MAX_FIXED_VALUE;
|
|
const r = await testContract.abs(n).callAsync();
|
|
expect(r).to.bignumber.eq(n);
|
|
});
|
|
|
|
it('abs(MIN_FIXED) throws', async () => {
|
|
const n = MIN_FIXED_VALUE;
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
|
|
n,
|
|
);
|
|
const tx = testContract.abs(n).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('abs(int(-1)) == int(1)', async () => {
|
|
const n = -1;
|
|
const r = await testContract.abs(new BigNumber(n)).callAsync();
|
|
expect(r).to.bignumber.eq(1);
|
|
});
|
|
|
|
it('abs(int(1)) == int(1)', async () => {
|
|
const n = 1;
|
|
const r = await testContract.abs(new BigNumber(n)).callAsync();
|
|
expect(r).to.bignumber.eq(1);
|
|
});
|
|
});
|
|
|
|
describe('invert()', () => {
|
|
it('invert(1) == 1', async () => {
|
|
const n = 1;
|
|
const r = await testContract.invert(toFixed(n)).callAsync();
|
|
assertFixedEquals(r, n);
|
|
});
|
|
|
|
it('invert(n) == 1 / n', async () => {
|
|
const n = 1337.5912;
|
|
const r = await testContract.invert(toFixed(n)).callAsync();
|
|
assertFixedRoughlyEquals(r, 1 / n);
|
|
});
|
|
|
|
it('invert(-n) == -1 / n', async () => {
|
|
const n = -1337.5912;
|
|
const r = await testContract.invert(toFixed(n)).callAsync();
|
|
assertFixedRoughlyEquals(r, 1 / n);
|
|
});
|
|
|
|
it('invert(0) throws', async () => {
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
|
|
);
|
|
const tx = testContract.invert(toFixed(0)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('mulDiv()', () => {
|
|
it('mulDiv(0, 0, 1) == 0', async () => {
|
|
const [a, n, d] = [0, 0, 1];
|
|
const r = await testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('mulDiv(0, x, y) == 0', async () => {
|
|
const [a, n, d] = [0, 13, 300];
|
|
const r = await testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('mulDiv(x, y, y) == x', async () => {
|
|
const [a, n, d] = [1.2345, 149, 149];
|
|
const r = await testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, a);
|
|
});
|
|
|
|
it('mulDiv(x, -y, y) == -x', async () => {
|
|
const [a, n, d] = [1.2345, -149, 149];
|
|
const r = await testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, -a);
|
|
});
|
|
|
|
it('mulDiv(-x, -y, y) == x', async () => {
|
|
const [a, n, d] = [-1.2345, -149, 149];
|
|
const r = await testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, -a);
|
|
});
|
|
|
|
it('mulDiv(x, y, 0) throws', async () => {
|
|
const [a, n, d] = [1.2345, 149, 0];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
|
|
);
|
|
const tx = testContract.mulDiv(toFixed(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('mulDiv(int(-1), int(1), int(-1)) == int(1)', async () => {
|
|
const [a, n, d] = [-1, 1, -1];
|
|
const r = await testContract.mulDiv(new BigNumber(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, fromFixed(1));
|
|
});
|
|
|
|
it('mulDiv(int(1), int(-1), int(-1)) == int(1)', async () => {
|
|
const [a, n, d] = [1, -1, -1];
|
|
const r = await testContract.mulDiv(new BigNumber(a), new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, fromFixed(1));
|
|
});
|
|
|
|
it('mulDiv(MIN_FIXED, int(-1), int(1)) throws', async () => {
|
|
const [a, n, d] = [MIN_FIXED_VALUE, -1, 1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
n,
|
|
);
|
|
const tx = testContract.mulDiv(a, new BigNumber(n), new BigNumber(d)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('mulDiv(int(-1), MIN_FIXED, int(1)) throws', async () => {
|
|
const [a, n, d] = [-1, MIN_FIXED_VALUE, 1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
n,
|
|
);
|
|
const tx = testContract.mulDiv(new BigNumber(a), n, new BigNumber(d)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('mulDiv(MIN_FIXED, int(1), int(-1)) throws', async () => {
|
|
const [a, n, d] = [MIN_FIXED_VALUE, 1, -1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionOverflow,
|
|
a,
|
|
d,
|
|
);
|
|
const tx = testContract.mulDiv(a, new BigNumber(n), new BigNumber(d)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('mulDiv(MAX_FIXED, int(-1), int(1)) == -MAX_FIXED', async () => {
|
|
const [a, n, d] = [MAX_FIXED_VALUE, -1, 1];
|
|
const r = await testContract.mulDiv(a, new BigNumber(n), new BigNumber(d)).callAsync();
|
|
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated());
|
|
});
|
|
|
|
it('mulDiv(MAX_FIXED, int(1), int(-1)) == -MAX_FIXED', async () => {
|
|
const [a, n, d] = [MAX_FIXED_VALUE, 1, -1];
|
|
const r = await testContract.mulDiv(a, new BigNumber(n), new BigNumber(d)).callAsync();
|
|
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated());
|
|
});
|
|
});
|
|
|
|
describe('add()', () => {
|
|
function add(a: Numberish, b: Numberish): BigNumber {
|
|
return fromFixed(toFixed(a).plus(toFixed(b)));
|
|
}
|
|
|
|
it('0 + 0 == 0', async () => {
|
|
const [a, b] = [0, 0];
|
|
const r = await testContract.add(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('adds two positive decimals', async () => {
|
|
const [a, b] = ['9310841.31841', '491021921.318948193'];
|
|
const r = await testContract.add(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, add(a, b));
|
|
});
|
|
|
|
it('adds two mixed decimals', async () => {
|
|
const [a, b] = ['9310841.31841', '-491021921.318948193'];
|
|
const r = await testContract.add(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, add(a, b));
|
|
});
|
|
|
|
it('throws on overflow', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(1)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.add(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws on underflow', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(-1)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.add(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MIN_FIXED + MIN_FIXED throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.add(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED + MAX_FIXED throws', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.add(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MIN_FIXED + MAX_FIXED == int(-1)', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, MAX_FIXED_VALUE];
|
|
const r = await testContract.add(a, b).callAsync();
|
|
expect(r).to.bignumber.eq(-1);
|
|
});
|
|
|
|
it('MAX_FIXED + (MIN_FIXED + int(1)) == 0', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MIN_FIXED_VALUE.plus(1)];
|
|
const r = await testContract.add(a, b).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
});
|
|
|
|
describe('sub()', () => {
|
|
function sub(a: Numberish, b: Numberish): BigNumber {
|
|
return fromFixed(toFixed(a).minus(toFixed(b)));
|
|
}
|
|
|
|
it('0 - 0 == 0', async () => {
|
|
const [a, b] = [0, 0];
|
|
const r = await testContract.sub(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('subtracts two positive decimals', async () => {
|
|
const [a, b] = ['9310841.31841', '491021921.318948193'];
|
|
const r = await testContract.sub(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, sub(a, b));
|
|
});
|
|
|
|
it('subtracts two mixed decimals', async () => {
|
|
const [a, b] = ['9310841.31841', '-491021921.318948193'];
|
|
const r = await testContract.sub(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, sub(a, b));
|
|
});
|
|
|
|
it('throws on underflow', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(1)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b.negated(),
|
|
);
|
|
const tx = testContract.sub(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws on overflow', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(-1)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b.negated(),
|
|
);
|
|
const tx = testContract.sub(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MIN_FIXED - MIN_FIXED throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
|
|
// This fails because `-MIN_FIXED_VALUE == MIN_FIXED_VALUE` because of
|
|
// twos-complement.
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
|
|
b,
|
|
);
|
|
const tx = testContract.sub(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED - MAX_FIXED == 0', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
|
|
const r = await testContract.sub(a, b).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('MIN_FIXED - MAX_FIXED throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, MAX_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
a,
|
|
b.negated(),
|
|
);
|
|
const tx = testContract.sub(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED - MIN_FIXED throws', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MIN_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
|
|
b,
|
|
);
|
|
const tx = testContract.sub(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('mul()', () => {
|
|
function mul(a: Numberish, b: Numberish): BigNumber {
|
|
return fromFixed(
|
|
toFixed(a)
|
|
.times(toFixed(b))
|
|
.dividedToIntegerBy(FIXED_POINT_DIVISOR),
|
|
);
|
|
}
|
|
|
|
it('x * 0 == 0', async () => {
|
|
const [a, b] = [1337, 0];
|
|
const r = await testContract.mul(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, b);
|
|
});
|
|
|
|
it('x * 1 == x', async () => {
|
|
const [a, b] = [0.5, 1];
|
|
const r = await testContract.mul(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, a);
|
|
});
|
|
|
|
it('x * -1 == -x', async () => {
|
|
const [a, b] = [0.5, -1];
|
|
const r = await testContract.mul(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, -a);
|
|
});
|
|
|
|
it('multiplies two positive decimals', async () => {
|
|
const [a, b] = ['1.25394912112', '0.03413318948193'];
|
|
const r = await testContract.mul(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, mul(a, b));
|
|
});
|
|
|
|
it('multiplies two mixed decimals', async () => {
|
|
const [a, b] = ['1.25394912112', '-0.03413318948193'];
|
|
const r = await testContract.mul(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, mul(a, b));
|
|
});
|
|
|
|
it('throws on underflow', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(2)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws on overflow', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(2)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED * int(1) == MAX_FIXED / FIXED_1', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, 1];
|
|
const r = await testContract.mul(a, new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_1));
|
|
});
|
|
|
|
it('MAX_FIXED * int(2) throws', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, 2];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, new BigNumber(b)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED * MAX_FIXED throws', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MIN_FIXED * MIN_FIXED throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED * MIN_FIXED throws', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, MIN_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MIN_FIXED * int(-1) throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, -1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(a, new BigNumber(b)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('int(-1) * MIN_FIXED throws', async () => {
|
|
const [a, b] = [-1, MIN_FIXED_VALUE];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.mul(new BigNumber(a), b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED * int(-1) == -MAX_FIXED / FIXED_1', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, -1];
|
|
const r = await testContract.mul(a, new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated().dividedToIntegerBy(FIXED_1));
|
|
});
|
|
});
|
|
|
|
describe('div()', () => {
|
|
function div(a: Numberish, b: Numberish): BigNumber {
|
|
return fromFixed(
|
|
toFixed(a)
|
|
.times(FIXED_POINT_DIVISOR)
|
|
.dividedBy(toFixed(b)),
|
|
);
|
|
}
|
|
|
|
it('x / 0 throws', async () => {
|
|
const [a, b] = [1, 0];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
|
|
toFixed(a).times(FIXED_POINT_DIVISOR),
|
|
toFixed(b),
|
|
);
|
|
const tx = testContract.div(toFixed(a), toFixed(b)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('x / 1 == x', async () => {
|
|
const [a, b] = [1.41214552, 1];
|
|
const r = await testContract.div(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, a);
|
|
});
|
|
|
|
it('x / -1 == -x', async () => {
|
|
const [a, b] = [1.109312, -1];
|
|
const r = await testContract.div(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, -a);
|
|
});
|
|
|
|
it('divides two positive decimals', async () => {
|
|
const [a, b] = ['1.25394912112', '0.03413318948193'];
|
|
const r = await testContract.div(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, div(a, b));
|
|
});
|
|
|
|
it('divides two mixed decimals', async () => {
|
|
const [a, b] = ['1.25394912112', '-0.03413318948193'];
|
|
const r = await testContract.div(toFixed(a), toFixed(b)).callAsync();
|
|
assertFixedEquals(r, div(a, b));
|
|
});
|
|
|
|
it('MIN_FIXED / int(-1) throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, -1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
FIXED_1,
|
|
);
|
|
const tx = testContract.div(a, new BigNumber(b)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('MAX_FIXED / int(-1) throws', async () => {
|
|
const [a, b] = [MIN_FIXED_VALUE, -1];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
FIXED_1,
|
|
);
|
|
const tx = testContract.div(a, new BigNumber(b)).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('int(-1) / MIN_FIXED == 0', async () => {
|
|
const [a, b] = [-1, MIN_FIXED_VALUE];
|
|
const r = await testContract.div(new BigNumber(a), b).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
});
|
|
|
|
describe('uintMul()', () => {
|
|
it('0 * x == 0', async () => {
|
|
const [a, b] = [0, 1234];
|
|
const r = await testContract.uintMul(toFixed(a), new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('1 * x == int(x)', async () => {
|
|
const [a, b] = [1, 1234];
|
|
const r = await testContract.uintMul(toFixed(a), new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(Math.trunc(b));
|
|
});
|
|
|
|
it('-1 * x == 0', async () => {
|
|
const [a, b] = [-1, 1234];
|
|
const r = await testContract.uintMul(toFixed(a), new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('0.5 * x == x/2', async () => {
|
|
const [a, b] = [0.5, 1234];
|
|
const r = await testContract.uintMul(toFixed(a), new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(b / 2);
|
|
});
|
|
|
|
it('0.5 * x == 0 if x = 1', async () => {
|
|
const [a, b] = [0.5, 1];
|
|
const r = await testContract.uintMul(toFixed(a), new BigNumber(b)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('throws if rhs is too large', async () => {
|
|
const [a, b] = [toFixed(1), MAX_FIXED_VALUE.plus(1)];
|
|
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
|
|
b,
|
|
);
|
|
const tx = testContract.uintMul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws if lhs is too large', async () => {
|
|
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(2)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
a,
|
|
b,
|
|
);
|
|
const tx = testContract.uintMul(a, b).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('toInteger()', () => {
|
|
it('toInteger(n) == int(n)', async () => {
|
|
const n = 1337.5912;
|
|
const r = await testContract.toInteger(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(Math.trunc(n));
|
|
});
|
|
|
|
it('toInteger(-n) == -int(n)', async () => {
|
|
const n = -1337.5912;
|
|
const r = await testContract.toInteger(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(Math.trunc(n));
|
|
});
|
|
|
|
it('toInteger(n) == 0, when 0 < n < 1', async () => {
|
|
const n = 0.9995;
|
|
const r = await testContract.toInteger(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('toInteger(-n) == 0, when -1 < n < 0', async () => {
|
|
const n = -0.9995;
|
|
const r = await testContract.toInteger(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
|
|
it('toInteger(0) == 0', async () => {
|
|
const n = 0;
|
|
const r = await testContract.toInteger(toFixed(n)).callAsync();
|
|
expect(r).to.bignumber.eq(0);
|
|
});
|
|
});
|
|
|
|
describe('toFixed()', () => {
|
|
describe('signed', () => {
|
|
it('converts a positive integer', async () => {
|
|
const n = 1337;
|
|
const r = await testContract.toFixedSigned1(new BigNumber(n)).callAsync();
|
|
assertFixedEquals(r, n);
|
|
});
|
|
|
|
it('converts a negative integer', async () => {
|
|
const n = -1337;
|
|
const r = await testContract.toFixedSigned1(new BigNumber(n)).callAsync();
|
|
assertFixedEquals(r, n);
|
|
});
|
|
|
|
it('converts a fraction with a positive numerator and denominator', async () => {
|
|
const [n, d] = [1337, 1000];
|
|
const r = await testContract.toFixedSigned2(new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, n / d);
|
|
});
|
|
|
|
it('converts a fraction with a negative numerator and positive denominator', async () => {
|
|
const [n, d] = [-1337, 1000];
|
|
const r = await testContract.toFixedSigned2(new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, n / d);
|
|
});
|
|
|
|
it('converts a fraction with a negative numerator and denominator', async () => {
|
|
const [n, d] = [-1337, -1000];
|
|
const r = await testContract.toFixedSigned2(new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, n / d);
|
|
});
|
|
|
|
it('converts a fraction with a negative numerator and negative denominator', async () => {
|
|
const [n, d] = [-1337, -1000];
|
|
const r = await testContract.toFixedSigned2(new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, n / d);
|
|
});
|
|
|
|
it('throws if the numerator is too large to convert', async () => {
|
|
const [n, d] = [MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_POINT_DIVISOR).plus(1), new BigNumber(1000)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
n,
|
|
FIXED_POINT_DIVISOR,
|
|
);
|
|
const tx = testContract.toFixedSigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws if the denominator is zero', async () => {
|
|
const [n, d] = [new BigNumber(1), new BigNumber(0)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
|
|
n.times(FIXED_POINT_DIVISOR),
|
|
d,
|
|
);
|
|
const tx = testContract.toFixedSigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('unsigned', () => {
|
|
it('converts an integer', async () => {
|
|
const n = 1337;
|
|
const r = await testContract.toFixedUnsigned1(new BigNumber(n)).callAsync();
|
|
assertFixedEquals(r, n);
|
|
});
|
|
|
|
it('converts a fraction', async () => {
|
|
const [n, d] = [1337, 1000];
|
|
const r = await testContract.toFixedUnsigned2(new BigNumber(n), new BigNumber(d)).callAsync();
|
|
assertFixedEquals(r, n / d);
|
|
});
|
|
|
|
it('throws if the numerator is too large', async () => {
|
|
const [n, d] = [MAX_FIXED_VALUE.plus(1), new BigNumber(1000)];
|
|
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
|
|
n,
|
|
);
|
|
const tx = testContract.toFixedUnsigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws if the denominator is too large', async () => {
|
|
const [n, d] = [new BigNumber(1000), MAX_FIXED_VALUE.plus(1)];
|
|
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
|
|
d,
|
|
);
|
|
const tx = testContract.toFixedUnsigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws if the numerator is too large to convert', async () => {
|
|
const [n, d] = [MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_POINT_DIVISOR).plus(1), new BigNumber(1000)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
|
|
n,
|
|
FIXED_POINT_DIVISOR,
|
|
);
|
|
const tx = testContract.toFixedUnsigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('throws if the denominator is zero', async () => {
|
|
const [n, d] = [new BigNumber(1), new BigNumber(0)];
|
|
const expectedError = new FixedMathRevertErrors.BinOpError(
|
|
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
|
|
n.times(FIXED_POINT_DIVISOR),
|
|
d,
|
|
);
|
|
const tx = testContract.toFixedUnsigned2(n, d).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
});
|
|
|
|
function getRandomDecimal(min: Numberish, max: Numberish): BigNumber {
|
|
const range = new BigNumber(max).minus(min);
|
|
const random = fromFixed(new BigNumber(hexRandom().substr(2), 16));
|
|
return random.mod(range).plus(min);
|
|
}
|
|
|
|
describe('ln()', () => {
|
|
const LN_PRECISION = 16;
|
|
|
|
function ln(x: Numberish): BigNumber {
|
|
return new BigNumber(
|
|
toDecimal(x)
|
|
.ln()
|
|
.toFixed(128),
|
|
);
|
|
}
|
|
|
|
it('ln(x = 0) throws', async () => {
|
|
const x = toFixed(0);
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
|
|
x,
|
|
);
|
|
const tx = testContract.ln(x).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('ln(x > 1) throws', async () => {
|
|
const x = toFixed(1.000001);
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
|
|
x,
|
|
);
|
|
const tx = testContract.ln(x).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('ln(x < 0) throws', async () => {
|
|
const x = toFixed(-0.000001);
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
|
|
x,
|
|
);
|
|
const tx = testContract.ln(x).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('ln(x = 1) == 0', async () => {
|
|
const x = toFixed(1);
|
|
const r = await testContract.ln(x).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('ln(x < LN_MIN_VAL) == EXP_MIN_VAL', async () => {
|
|
const x = toFixed(MIN_LN_NUMBER).minus(1);
|
|
const r = await testContract.ln(x).callAsync();
|
|
assertFixedEquals(r, MIN_EXP_NUMBER);
|
|
});
|
|
|
|
it('ln(x), where x is close to 0', async () => {
|
|
const x = new BigNumber('1e-27');
|
|
const r = await testContract.ln(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, ln(x), 12);
|
|
});
|
|
|
|
it('ln(x), where x is close to 1', async () => {
|
|
const x = new BigNumber(1).minus('1e-27');
|
|
const r = await testContract.ln(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
|
|
});
|
|
|
|
it('ln(x = 0.85)', async () => {
|
|
const x = 0.85;
|
|
const r = await testContract.ln(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
|
|
});
|
|
|
|
blockchainTests.optional('fuzzing', () => {
|
|
const inputs = _.times(FUZZ_COUNT, () => getRandomDecimal(0, 1));
|
|
for (const x of inputs) {
|
|
it(`ln(${x.toString(10)})`, async () => {
|
|
const r = await testContract.ln(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('exp()', () => {
|
|
const EXP_PRECISION = 18;
|
|
|
|
function exp(x: Numberish): BigNumber {
|
|
return new BigNumber(
|
|
toDecimal(x)
|
|
.exp()
|
|
.toFixed(128),
|
|
);
|
|
}
|
|
|
|
it('exp(x = 0) == 1', async () => {
|
|
const x = toFixed(0);
|
|
const r = await testContract.exp(x).callAsync();
|
|
assertFixedEquals(r, 1);
|
|
});
|
|
|
|
it('exp(x > EXP_MAX_VAL) throws', async () => {
|
|
const x = toFixed(MAX_EXP_NUMBER).plus(1);
|
|
const expectedError = new FixedMathRevertErrors.SignedValueError(
|
|
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
|
|
x,
|
|
);
|
|
const tx = testContract.exp(x).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('exp(x < EXP_MIN_VAL) == 0', async () => {
|
|
const x = toFixed(MIN_EXP_NUMBER).minus(1);
|
|
const r = await testContract.exp(x).callAsync();
|
|
assertFixedEquals(r, 0);
|
|
});
|
|
|
|
it('exp(x < 0), where x is close to 0', async () => {
|
|
const x = new BigNumber('-1e-18');
|
|
const r = await testContract.exp(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
|
|
});
|
|
|
|
it('exp(x), where x is close to EXP_MIN_VAL', async () => {
|
|
const x = MIN_EXP_NUMBER.plus('1e-18');
|
|
const r = await testContract.exp(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
|
|
});
|
|
|
|
it('exp(x = -0.85)', async () => {
|
|
const x = -0.85;
|
|
const r = await testContract.exp(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
|
|
});
|
|
|
|
blockchainTests.optional('fuzzing', () => {
|
|
const inputs = _.times(FUZZ_COUNT, () => getRandomDecimal(MIN_EXP_NUMBER, MAX_EXP_NUMBER));
|
|
for (const x of inputs) {
|
|
it(`exp(${x.toString(10)})`, async () => {
|
|
const r = await testContract.exp(toFixed(x)).callAsync();
|
|
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable-next-line: max-file-line-count
|