Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Merge branch 'feature/3.0/exchange/fill-order-unit-tests' into 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dorothy-zbornak committed Aug 10, 2019
2 parents f757a9d + 18485dd commit 8adfa52
Show file tree
Hide file tree
Showing 64 changed files with 3,130 additions and 1,169 deletions.
71 changes: 48 additions & 23 deletions contracts/exchange-libs/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
[
{
"timestamp": 1563193019,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"version": "3.1.0",
"changes": [
{
"note": "Move `LibTransactionDecoder` to contracts/dev-utils package",
"pr": 1848
},
{
"note": "Break up `LibEIP712` into reusable components",
"pr": 1742
Expand Down Expand Up @@ -75,6 +53,53 @@
{
"note": "Add `expirationTimeSeconds` to `ZeroExTransaction` struct",
"pr": 1823
},
{
"note": "Add reference functions for `LibMath` and `LibFillResults`",
"pr": 2031
},
{
"note": "Move in revamped `LibMath` tests from the `contracts-exchange` package.",
"pr": 2031
},
{
"note": "Move in revamped `LibFillResults` tests from the `contracts-exchange` package.",
"pr": 2031
},
{
"note": "Remove unecessary zero-denominator checks in `LibMath`.",
"pr": 2031
},
{
"note": "Fix coverage hooks.",
"pr": 2031
}
]
},
{
"timestamp": 1563193019,
"version": "3.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1563047529,
"version": "3.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.0.0",
"changes": [
{
"note": "Move `LibTransactionDecoder` to contracts/dev-utils package",
"pr": 1848
}
],
"timestamp": 1563006338
Expand Down
16 changes: 0 additions & 16 deletions contracts/exchange-libs/contracts/src/LibMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
}

if (_isRoundingErrorFloor(
numerator,
denominator,
Expand Down Expand Up @@ -79,10 +75,6 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
}

if (_isRoundingErrorCeil(
numerator,
denominator,
Expand Down Expand Up @@ -122,10 +114,6 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
}

partialAmount = _safeDiv(
_safeMul(numerator, target),
denominator
Expand All @@ -147,10 +135,6 @@ contract LibMath is
pure
returns (uint256 partialAmount)
{
if (denominator == 0) {
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
}

// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using _safeDiv.
Expand Down
34 changes: 34 additions & 0 deletions contracts/exchange-libs/contracts/test/TestLibs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,40 @@ contract TestLibs is
return partialAmount;
}

function safeGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _safeGetPartialAmountFloor(
numerator,
denominator,
target
);
return partialAmount;
}

function safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
public
pure
returns (uint256 partialAmount)
{
partialAmount = _safeGetPartialAmountCeil(
numerator,
denominator,
target
);
return partialAmount;
}

function isRoundingErrorFloor(
uint256 numerator,
uint256 denominator,
Expand Down
1 change: 1 addition & 0 deletions contracts/exchange-libs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@0x/contracts-test-utils": "^3.1.10",
"@0x/dev-utils": "^2.2.4",
"@0x/sol-compiler": "^3.1.9",
"@0x/subproviders": "^4.1.1",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/node": "*",
Expand Down
3 changes: 3 additions & 0 deletions contracts/exchange-libs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './artifacts';
export * from './wrappers';

import * as ReferenceFunctionsToExport from './reference_functions';
export import ReferenceFunctions = ReferenceFunctionsToExport;
89 changes: 89 additions & 0 deletions contracts/exchange-libs/src/reference_functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ReferenceFunctions } from '@0x/contracts-utils';
import { LibMathRevertErrors } from '@0x/order-utils';
import { FillResults } from '@0x/types';
import { BigNumber } from '@0x/utils';

const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;

/**
* Checks if rounding error >= 0.1% when rounding down.
*/
export function isRoundingErrorFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
if (denominator.eq(0)) {
throw new LibMathRevertErrors.DivisionByZeroError();
}
if (numerator.eq(0) || target.eq(0)) {
return false;
}
const remainder = numerator.times(target).mod(denominator);
// Need to do this separately because solidity evaluates RHS of the comparison expression first.
const rhs = safeMul(numerator, target);
const lhs = safeMul(new BigNumber(1000), remainder);
return lhs.gte(rhs);
}

/**
* Checks if rounding error >= 0.1% when rounding up.
*/
export function isRoundingErrorCeil(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
if (denominator.eq(0)) {
throw new LibMathRevertErrors.DivisionByZeroError();
}
if (numerator.eq(0) || target.eq(0)) {
return false;
}
let remainder = numerator.times(target).mod(denominator);
remainder = safeSub(denominator, remainder).mod(denominator);
// Need to do this separately because solidity evaluates RHS of the comparison expression first.
const rhs = safeMul(numerator, target);
const lhs = safeMul(new BigNumber(1000), remainder);
return lhs.gte(rhs);
}

/**
* Calculates partial value given a numerator and denominator rounded down.
* Reverts if rounding error is >= 0.1%
*/
export function safeGetPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
if (isRoundingErrorFloor(numerator, denominator, target)) {
throw new LibMathRevertErrors.RoundingError(numerator, denominator, target);
}
return safeDiv(safeMul(numerator, target), denominator);
}

/**
* Calculates partial value given a numerator and denominator rounded down.
* Reverts if rounding error is >= 0.1%
*/
export function safeGetPartialAmountCeil(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
if (isRoundingErrorCeil(numerator, denominator, target)) {
throw new LibMathRevertErrors.RoundingError(numerator, denominator, target);
}
return safeDiv(safeAdd(safeMul(numerator, target), safeSub(denominator, new BigNumber(1))), denominator);
}

/**
* Calculates partial value given a numerator and denominator rounded down.
*/
export function getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
return safeDiv(safeMul(numerator, target), denominator);
}

/**
* Calculates partial value given a numerator and denominator rounded down.
*/
export function getPartialAmountCeil(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
return safeDiv(safeAdd(safeMul(numerator, target), safeSub(denominator, new BigNumber(1))), denominator);
}

/**
* Adds properties of two `FillResults`.
*/
export function addFillResults(a: FillResults, b: FillResults): FillResults {
return {
makerAssetFilledAmount: safeAdd(a.makerAssetFilledAmount, b.makerAssetFilledAmount),
takerAssetFilledAmount: safeAdd(a.takerAssetFilledAmount, b.takerAssetFilledAmount),
makerFeePaid: safeAdd(a.makerFeePaid, b.makerFeePaid),
takerFeePaid: safeAdd(a.takerFeePaid, b.takerFeePaid),
};
}
18 changes: 14 additions & 4 deletions contracts/exchange-libs/test/global_hooks.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { env, EnvVars } from '@0x/dev-utils';

import { coverage, profiler, provider } from '@0x/contracts-test-utils';
import { env, EnvVars } from '@0x/dev-utils';
import { prependSubprovider } from '@0x/subproviders';
import { providerUtils } from '@0x/utils';

const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();

if (env.parseBoolean(EnvVars.SolidityCoverage)) {
prependSubprovider(provider, coverageSubprovider);
provider.stop();
}
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
prependSubprovider(provider, profilerSubprovider);
provider.stop();
}

before('start web3 provider', () => {
providerUtils.startProviderEngine(provider);
});
after('generate coverage report', async () => {
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
await coverageSubprovider.writeCoverageAsync();
}
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
await profilerSubprovider.writeProfilerOutputAsync();
}
provider.stop();
Expand Down
90 changes: 90 additions & 0 deletions contracts/exchange-libs/test/lib_fill_results.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { blockchainTests, constants, describe, expect } from '@0x/contracts-test-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 () => {
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
env.provider,
env.txDefaults,
new BigNumber(CHAIN_ID),
);
});

describe('addFillResults', () => {
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),
},
];

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);
});

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);
});

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);
});
});
});
});
Loading

0 comments on commit 8adfa52

Please sign in to comment.