protocol/contracts/utils/src/reference_functions.ts
Jacob Evans 68656b4a4d
chore: Prune old contracts (#304)
Prune exchange-libs
deploy migrations at specific address
remove exchange-libs, moving LibMath into Utils
Remove staking order-utils multisig and remaining asset-proxy
2021-08-18 10:33:47 +10:00

129 lines
4.4 KiB
TypeScript

import { BigNumber, LibMathRevertErrors, SafeMathRevertErrors } from '@0x/utils';
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
/**
* Add two `uint256` values. Reverts on overflow.
*/
export function safeAdd(a: BigNumber, b: BigNumber): BigNumber {
const r = a.plus(b);
if (r.isGreaterThan(MAX_UINT256)) {
throw new SafeMathRevertErrors.Uint256BinOpError(SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow, a, b);
}
return r;
}
/**
* Subract two `uint256` values. Reverts on overflow.
*/
export function safeSub(a: BigNumber, b: BigNumber): BigNumber {
const r = a.minus(b);
if (r.isLessThan(0)) {
throw new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
a,
b,
);
}
return r;
}
/**
* Multiplies two `uint256` values. Reverts on overflow.
*/
export function safeMul(a: BigNumber, b: BigNumber): BigNumber {
const r = a.times(b);
if (r.isGreaterThan(MAX_UINT256)) {
throw new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
}
return r;
}
/**
* Divides two `uint256` values. Reverts on division by zero.
*/
export function safeDiv(a: BigNumber, b: BigNumber): BigNumber {
if (b.isEqualTo(0)) {
throw new SafeMathRevertErrors.Uint256BinOpError(SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero, a, b);
}
return a.dividedToIntegerBy(b);
}
// LibMath
/**
* 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(remainder, new BigNumber(1000));
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(remainder, new BigNumber(1000));
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 {
const sub = safeSub(denominator, new BigNumber(1)); // This is computed first to simulate Solidity's order of operations
return safeDiv(safeAdd(safeMul(numerator, target), sub), denominator);
}