protocol/contracts/exchange/test/utils/reference_functions.ts
Lawrence Forman c54d69e5ae @0x/contracts-exchange: Create reference functions test util.
`@0x/contracts-exchange`: Use reference functions to assert fill results
in `isolated_fill_order` tests.
2019-08-06 15:27:39 -04:00

108 lines
3.2 KiB
TypeScript

import { constants, FillResults } from '@0x/contracts-test-utils';
import { LibMathRevertErrors } from '@0x/order-utils';
import { OrderWithoutDomain } from '@0x/types';
import { AnyRevertError, BigNumber, SafeMathRevertErrors } from '@0x/utils';
const { MAX_UINT256 } = constants;
export function isRoundingErrorFloor(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): boolean {
if (denominator.eq(0)) {
throw new LibMathRevertErrors.DivisionByZeroError();
}
if (numerator.eq(0)) {
return false;
}
if (target.eq(0)) {
return false;
}
const product = numerator.multipliedBy(target);
const remainder = product.mod(denominator);
const remainderTimes1000 = remainder.multipliedBy('1000');
const isError = remainderTimes1000.gte(product);
if (remainderTimes1000.isGreaterThan(MAX_UINT256)) {
// Solidity implementation won't actually throw.
throw new AnyRevertError();
}
return isError;
}
export function IsRoundingErrorCeil(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): boolean {
if (denominator.eq(0)) {
throw new LibMathRevertErrors.DivisionByZeroError();
}
if (numerator.eq(0)) {
return false;
}
if (target.eq(0)) {
return false;
}
const product = numerator.multipliedBy(target);
const remainder = product.mod(denominator);
const error = denominator.minus(remainder).mod(denominator);
const errorTimes1000 = error.multipliedBy('1000');
const isError = errorTimes1000.gte(product);
if (errorTimes1000.isGreaterThan(MAX_UINT256)) {
// Solidity implementation won't actually throw.
throw new AnyRevertError();
}
return isError;
}
export function safeGetPartialAmountFloor(
numerator: BigNumber,
denominator: BigNumber,
target: BigNumber,
): BigNumber {
if (denominator.eq(0)) {
throw new LibMathRevertErrors.DivisionByZeroError();
}
const isRoundingError = isRoundingErrorFloor(numerator, denominator, target);
if (isRoundingError) {
throw new LibMathRevertErrors.RoundingError(numerator, denominator, target);
}
const product = numerator.multipliedBy(target);
if (product.isGreaterThan(MAX_UINT256)) {
throw new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
numerator,
denominator,
);
}
return product.dividedToIntegerBy(denominator);
}
export function calculateFillResults(
order: OrderWithoutDomain,
takerAssetFilledAmount: BigNumber,
): FillResults {
const makerAssetFilledAmount = safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const makerFeePaid = safeGetPartialAmountFloor(
makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee,
);
const takerFeePaid = safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee,
);
return {
makerAssetFilledAmount,
takerAssetFilledAmount,
makerFeePaid,
takerFeePaid,
};
}