`@0x/contracts-exchange`: Use reference functions to assert fill results in `isolated_fill_order` tests.
108 lines
3.2 KiB
TypeScript
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,
|
|
};
|
|
}
|