@0x/contracts-staking: Add rich reverts.

`@0x/utils`: Add `LibFixedMath` `RevertError` types.
`@0x/order-utils`: Add `InvalidCobbDouglasAlphaerror` `RevertError` type.
This commit is contained in:
Lawrence Forman 2019-08-27 15:16:59 -04:00 committed by Lawrence Forman
parent a09cd03ce6
commit 7b5e3dab17
15 changed files with 420 additions and 20 deletions

View File

@ -1,6 +1,6 @@
/*
Copyright 2018 ZeroEx Intl.
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -68,10 +68,11 @@ contract MixinExchangeFees is
external
onlyOwner
{
if (int256(numerator) < 0 ||
int256(denominator) <= 0 ||
numerator > denominator) {
revert("INVALID_ALPHA");
if (int256(numerator) < 0 || int256(denominator) <= 0 || numerator > denominator) {
LibRichErrors.rrevert(LibStakingRichErrors.InvalidCobbDouglasAlphaError(
numerator,
denominator
));
}
cobbDouglasAlphaNumerator = numerator;
cobbDouglasAlphaDenomintor = denominator;
@ -307,9 +308,7 @@ contract MixinExchangeFees is
pure
returns (uint256 ownerRewards)
{
assert(ownerFees <= totalFees);
assert(ownerStake <= totalStake);
assert(alphaNumerator <= alphaDenominator);
assert(alphaNumerator > alphaDenominator);
int256 feeRatio = LibFixedMath._toFixed(ownerFees, totalFees);
int256 stakeRatio = LibFixedMath._toFixed(ownerStake, totalStake);
int256 alpha = LibFixedMath._toFixed(alphaNumerator, alphaDenominator);
@ -323,7 +322,7 @@ contract MixinExchangeFees is
int256 n = LibFixedMath._exp(
LibFixedMath._mul(
alpha,
LibFixedMath._ln(feeRatio) - LibFixedMath._ln(stakeRatio)
LibFixedMath._sub(LibFixedMath._ln(feeRatio), LibFixedMath._ln(stakeRatio))
)
);
// Multiply the above with totalRewards * stakeRatio

View File

@ -18,6 +18,7 @@
pragma solidity ^0.5.9;
import "./LibFixedMathRichErrors.sol";
/// @dev Signed, fixed-point, 127-bit precision math library.
library LibFixedMath {
@ -31,8 +32,18 @@ library LibFixedMath {
int256 private constant EXP_MIN_VAL = int256(0xffffffffffffffffffffffffffffffdd7612c00c0077ada1b83518e8cafc0e90);
/// @dev Get one as a fixed-point number.
function _one() internal pure returns (int256 c) {
c = FIXED_1;
function _one() internal pure returns (int256 f) {
f = FIXED_1;
}
/// @dev Returns the addition of two fixed point numbers, reverting on overflow.
function _add(int256 a, int256 b) internal pure returns (int256 c) {
c = __add(a, -b, LibFixedMathRichErrors.BinOpErrorCodes.SUBTRACTION_OVERFLOW);
}
/// @dev Returns the addition of two fixed point numbers, reverting on overflow.
function _sub(int256 a, int256 b) internal pure returns (int256 c) {
c = __add(a, -b, LibFixedMathRichErrors.BinOpErrorCodes.SUBTRACTION_OVERFLOW);
}
/// @dev Returns the multiplication of two fixed point numbers, reverting on overflow.
@ -49,7 +60,12 @@ library LibFixedMath {
/// number with an integer, reverting if the multiplication overflows.
/// Negative results are clamped to zero.
function _uintMul(int256 f, uint256 u) internal pure returns (uint256) {
require(int256(u) >= int256(0), "FIXED_MATH_INTEGER_TOO_LARGE");
if (int256(u) < int256(0)) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathUnsignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
u
));
}
int256 c = __mul(f, int256(u));
if (c <= 0) {
return 0;
@ -79,14 +95,30 @@ library LibFixedMath {
/// @dev Convert unsigned `n` / 1 to a fixed-point number.
/// Reverts if `n` is too large to fit in a fixed-point number.
function _toFixed(uint256 n) internal pure returns (int256 f) {
require(int256(n) >= 0, "FIXED_MATH_INTEGER_TOO_LARGE");
if (int256(n) < int256(0)) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathUnsignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
n
));
}
f = __mul(int256(n), FIXED_1);
}
/// @dev Convert unsigned `n` / `d` to a fixed-point number.
/// Reverts if `n` / `d` is too large to fit in a fixed-point number.
function _toFixed(uint256 n, uint256 d) internal pure returns (int256 f) {
require(int256(n) >= 0 && int256(d) >= 0, "FIXED_MATH_INTEGER_TOO_LARGE");
if (int256(n) < int256(0)) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathUnsignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
n
));
}
if (int256(d) < int256(0)) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathUnsignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
d
));
}
f = __div(__mul(int256(n), FIXED_1), int256(d));
}
@ -100,7 +132,18 @@ library LibFixedMath {
if (x == FIXED_1) {
return 0;
}
assert(x < LN_MAX_VAL && x > 0);
if (x > LN_MAX_VAL) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathSignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
x
));
}
if (x <= 0) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathSignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_SMALL,
x
));
}
int256 y;
int256 z;
@ -177,7 +220,12 @@ library LibFixedMath {
if (x == 0) {
return FIXED_1;
}
assert(x < 0);
if (x > 0) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathSignedValueError(
LibFixedMathRichErrors.ValueErrorCodes.TOO_LARGE,
x
));
}
// Rewrite the input as a product of positive natural exponents and a
// single residual q, where q is a number of small magnitude.
@ -269,12 +317,44 @@ library LibFixedMath {
return 0;
}
c = a * b;
require(c / a == b, "FIXED_MATH_MULTIPLICATION_OVERFLOW");
if (c / a != b) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathBinOpError(
LibFixedMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
}
}
/// @dev Returns the division of two numbers, reverting on division by zero.
function __div(int256 a, int256 b) private pure returns (int256 c) {
require(b != 0, "FIXED_MATH_DIVISION_BY_ZERO");
if (b == 0) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathBinOpError(
LibFixedMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
}
c = a / b;
}
/// @dev Adds two numbers, reverting on overflow.
function __add(
int256 a,
int256 b,
LibFixedMathRichErrors.BinOpErrorCodes errorCode
)
private
pure
returns (int256 c)
{
c = a + b;
if ((c > 0 && a < 0 && b < 0) || (c < 0 && a > 0 && b > 0)) {
LibRichErrors.rrevert(LibFixedMathRichErrors.FixedMathBinOpError(
errorCode,
a,
b
));
}
}
}

View File

@ -0,0 +1,97 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
library LibFixedMathRichErrors {
enum ValueErrorCodes {
TOO_SMALL,
TOO_LARGE
}
enum BinOpErrorCodes {
ADDITION_OVERFLOW,
SUBTRACTION_OVERFLOW,
MULTIPLICATION_OVERFLOW,
DIVISION_BY_ZERO
}
// bytes4(keccak256("FixedMathSignedValueError(uint8,int256)"))
bytes4 internal constant SIGNED_VALUE_ERROR_SELECTOR =
0x0edd9c46;
// bytes4(keccak256("FixedMathUnsignedValueError(uint8,uint256)"))
bytes4 internal constant UNSIGNED_VALUE_ERROR_SELECTOR =
0x38e93856;
// bytes4(keccak256("FixedMathBinOpError(uint8,int256,int256)"))
bytes4 internal constant BIN_OP_ERROR_SELECTOR =
0x2bd3386e;
// solhint-disable func-name-mixedcase
function FixedMathSignedValueError(
ValueErrorCodes error,
int256 n
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNED_VALUE_ERROR_SELECTOR,
uint8(error),
n
);
}
function FixedMathUnsignedValueError(
ValueErrorCodes error,
uint256 n
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UNSIGNED_VALUE_ERROR_SELECTOR,
uint8(error),
n
);
}
function FixedMathBinOpError(
BinOpErrorCodes error,
int256 a,
int256 b
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
BIN_OP_ERROR_SELECTOR,
uint8(error),
a,
b
);
}
}

View File

@ -118,6 +118,10 @@ library LibStakingRichErrors {
bytes4 internal constant POOL_ALREADY_EXISTS_ERROR_SELECTOR =
0x2a5e4dcf;
// bytes4(keccak256("InvalidCobbDouglasAlphaError(uint256,uint256)"))
bytes4 internal constant INVALID_COBB_DOUGLAS_ALPHA_ERROR_SELECTOR =
0x8f8e73de;
// solhint-disable func-name-mixedcase
function MiscalculatedRewardsError(
uint256 totalRewardsPaid,
@ -443,4 +447,19 @@ library LibStakingRichErrors {
poolId
);
}
function InvalidCobbDouglasAlphaError(
uint256 numerator,
uint256 denominator
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_COBB_DOUGLAS_ALPHA_ERROR_SELECTOR,
numerator,
denominator
);
}
}

View File

@ -0,0 +1,49 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/Staking.sol";
contract TestCobbDouglas is
Staking
{
function cobbDouglas(
uint256 totalRewards,
uint256 ownerFees,
uint256 totalFees,
uint256 ownerStake,
uint256 totalStake,
uint256 alphaNumerator,
uint256 alphaDenominator
)
external
pure
returns (uint256 ownerRewards)
{
ownerRewards = _cobbDouglas(
totalRewards,
ownerFees,
totalFees,
ownerStake,
totalStake,
alphaNumerator,
alphaDenominator
);
}
}

View File

@ -0,0 +1,80 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/libs/LibFixedMath.sol";
contract TestLibFixedMath {
function one() external pure returns (int256) {
return LibFixedMath._one();
}
function mul(int256 a, int256 b) external pure returns (int256) {
return LibFixedMath._mul(a, b);
}
function div(int256 a, int256 b) external pure returns (int256) {
return LibFixedMath._div(a, b);
}
function add(int256 a, int256 b) external pure returns (int256) {
return LibFixedMath._add(a, b);
}
function sub(int256 a, int256 b) external pure returns (int256) {
return LibFixedMath._sub(a, b);
}
function uintMul(int256 f, uint256 u) internal pure returns (uint256) {
return LibFixedMath._uintMul(f, u);
}
function abs(int256 a) external pure returns (int256) {
return LibFixedMath._abs(a);
}
function toFixedSigned(int256 n, int256 d) external pure returns (int256) {
return LibFixedMath._toFixed(n, d);
}
function toFixedSigned(int256 n) external pure returns (int256) {
return LibFixedMath._toFixed(n);
}
function toFixedUnsigned(uint256 n, uint256 d) external pure returns (int256) {
return LibFixedMath._toFixed(n, d);
}
function toFixedUnsigned(uint256 n) external pure returns (int256) {
return LibFixedMath._toFixed(n);
}
function toInteger(int256 f) external pure returns (int256) {
return LibFixedMath._toInteger(f);
}
function ln(int256 x) internal pure returns (int256 r) {
return LibFixedMath._ln(x);
}
function exp(int256 x) internal pure returns (int256 r) {
return LibFixedMath._exp(x);
}
}

View File

@ -36,7 +36,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingPoolRewardVault|IStakingProxy|IStructs|IVaultCore|IWallet|IZrxVault|LibEIP712Hash|LibFixedMath|LibRewardMath|LibSafeMath|LibSafeMath64|LibSafeMath96|LibSignatureValidator|LibStakingRichErrors|MixinConstants|MixinDelegatedStake|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinOwnable|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakingPool|MixinStakingPoolRewardVault|MixinStakingPoolRewards|MixinStorage|MixinTimeLockedStake|MixinVaultCore|MixinZrxVault|Staking|StakingPoolRewardVault|StakingProxy|ZrxVault).json",
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingPoolRewardVault|IStakingProxy|IStructs|IVaultCore|IWallet|IZrxVault|LibEIP712Hash|LibFixedMath|LibFixedMathRichErrors|LibRewardMath|LibSafeMath|LibSafeMath64|LibSafeMath96|LibSignatureValidator|LibStakingRichErrors|MixinConstants|MixinDelegatedStake|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinOwnable|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakingPool|MixinStakingPoolRewardVault|MixinStakingPoolRewards|MixinStorage|MixinTimeLockedStake|MixinVaultCore|MixinZrxVault|Staking|StakingPoolRewardVault|StakingProxy|TestCobbDouglas|TestLibFixedMath|ZrxVault).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -15,6 +15,7 @@ import * as IWallet from '../generated-artifacts/IWallet.json';
import * as IZrxVault from '../generated-artifacts/IZrxVault.json';
import * as LibEIP712Hash from '../generated-artifacts/LibEIP712Hash.json';
import * as LibFixedMath from '../generated-artifacts/LibFixedMath.json';
import * as LibFixedMathRichErrors from '../generated-artifacts/LibFixedMathRichErrors.json';
import * as LibRewardMath from '../generated-artifacts/LibRewardMath.json';
import * as LibSafeDowncast from '../generated-artifacts/LibSafeDowncast.json';
import * as LibSignatureValidator from '../generated-artifacts/LibSignatureValidator.json';
@ -28,8 +29,8 @@ import * as MixinScheduler from '../generated-artifacts/MixinScheduler.json';
import * as MixinStake from '../generated-artifacts/MixinStake.json';
import * as MixinStakeBalances from '../generated-artifacts/MixinStakeBalances.json';
import * as MixinStakingPool from '../generated-artifacts/MixinStakingPool.json';
import * as MixinStakingPoolRewardVault from '../generated-artifacts/MixinStakingPoolRewardVault.json';
import * as MixinStakingPoolRewards from '../generated-artifacts/MixinStakingPoolRewards.json';
import * as MixinStakingPoolRewardVault from '../generated-artifacts/MixinStakingPoolRewardVault.json';
import * as MixinStorage from '../generated-artifacts/MixinStorage.json';
import * as MixinTimeLockedStake from '../generated-artifacts/MixinTimeLockedStake.json';
import * as MixinVaultCore from '../generated-artifacts/MixinVaultCore.json';
@ -57,6 +58,7 @@ export const artifacts = {
IZrxVault: IZrxVault as ContractArtifact,
LibEIP712Hash: LibEIP712Hash as ContractArtifact,
LibFixedMath: LibFixedMath as ContractArtifact,
LibFixedMathRichErrors: LibFixedMathRichErrors as ContractArtifact,
LibRewardMath: LibRewardMath as ContractArtifact,
LibSafeDowncast: LibSafeDowncast as ContractArtifact,
LibSignatureValidator: LibSignatureValidator as ContractArtifact,

View File

@ -13,6 +13,7 @@ export * from '../generated-wrappers/i_wallet';
export * from '../generated-wrappers/i_zrx_vault';
export * from '../generated-wrappers/lib_e_i_p712_hash';
export * from '../generated-wrappers/lib_fixed_math';
export * from '../generated-wrappers/lib_fixed_math_rich_errors';
export * from '../generated-wrappers/lib_reward_math';
export * from '../generated-wrappers/lib_safe_downcast';
export * from '../generated-wrappers/lib_signature_validator';

View File

@ -13,6 +13,7 @@
"generated-artifacts/IZrxVault.json",
"generated-artifacts/LibEIP712Hash.json",
"generated-artifacts/LibFixedMath.json",
"generated-artifacts/LibFixedMathRichErrors.json",
"generated-artifacts/LibRewardMath.json",
"generated-artifacts/LibSafeDowncast.json",
"generated-artifacts/LibSignatureValidator.json",

View File

@ -69,6 +69,10 @@
{
"note": "Add EIP712 types for Staking",
"pr": 1910
},
{
"note": "Add `InvalidCobbDouglasAlphaError` `RevertError` type to `StakingRevertErrors`",
"pr": "TODO"
}
]
},

View File

@ -195,6 +195,16 @@ export class PoolAlreadyExistsError extends RevertError {
}
}
export class InvalidCobbDouglasAlphaError extends RevertError {
constructor(numerator: BigNumber | number | string, denominator: BigNumber | number | string) {
super(
'InvalidCobbDouglasAlphaError',
'InvalidCobbDouglasAlphaError(uint256 numerator, uint256 denominator)',
{ numerator, denominator },
);
}
}
const types = [
MiscalculatedRewardsError,
OnlyCallableByExchangeError,
@ -220,6 +230,7 @@ const types = [
AmountExceedsBalanceOfPoolError,
OperatorShareMustBeBetween0And100Error,
PoolAlreadyExistsError,
InvalidCobbDouglasAlphaError,
];
// Register the types we've defined.

View File

@ -5,6 +5,10 @@
{
"note": "Allow for array types in `RevertError`s.",
"pr": 2075
},
{
"note": "Add `LibFixedMath` `RevertError` types.",
"pr": "TODO"
}
]
},

View File

@ -0,0 +1,51 @@
import { BigNumber } from './configured_bignumber';
import { RevertError } from './revert_error';
// tslint:disable:max-classes-per-file
export enum ValueErrorCodes {
TooSmall,
TooLarge,
}
export enum BinOpErrorCodes {
AdditionOverflow,
MultiplicationOverflow,
SubtractionUnderflow,
DivisionByZero,
}
export class FixedMathSignedValueError extends RevertError {
constructor(error?: ValueErrorCodes, n?: BigNumber | number | string) {
super('FixedMathSignedValueError', 'FixedMathSignedValueError(uint8 error, int256 n)', {
error,
n,
});
}
}
export class FixedMathUnsignedValueError extends RevertError {
constructor(error?: ValueErrorCodes, n?: BigNumber | number | string) {
super('FixedMathUnsignedValueError', 'FixedMathUnsignedValueError(uint8 error, uint256 n)', {
error,
n,
});
}
}
export class FixedMathBinOpError extends RevertError {
constructor(error?: BinOpErrorCodes, a?: BigNumber | number | string, b?: BigNumber | number | string) {
super('FixedMathBinOpError', 'FixedMathBinOpError(uint8 error, uint256 a, uint256 b)', {
error,
a,
b,
});
}
}
const types = [ FixedMathSignedValueError, FixedMathSignedValueError, FixedMathBinOpError ];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@ -1,4 +1,5 @@
import * as AuthorizableRevertErrors from './authorizable_revert_errors';
import * as FixedMathRevertErrors from './fixed_math_revert_errors';
import * as LibAddressArrayRevertErrors from './lib_address_array_revert_errors';
import * as LibBytesRevertErrors from './lib_bytes_revert_errors';
import * as OwnableRevertErrors from './ownable_revert_errors';
@ -34,6 +35,7 @@ export {
export {
AuthorizableRevertErrors,
FixedMathRevertErrors,
LibAddressArrayRevertErrors,
LibBytesRevertErrors,
OwnableRevertErrors,