Merge pull request #2103 from 0xProject/feature/contracts-staking/rich-reverts

Staking RichErrors and cleanup
This commit is contained in:
mzhu25
2019-08-26 17:32:05 -07:00
committed by GitHub
51 changed files with 1415 additions and 584 deletions

View File

@@ -1748,8 +1748,8 @@ describe('ERC1155Proxy', () => {
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
maxUintValue,
valueMultiplier,
);
@@ -1836,8 +1836,8 @@ describe('ERC1155Proxy', () => {
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
spenderInitialFungibleBalance,
valuesToTransfer[0].times(valueMultiplier),
);

View File

@@ -173,8 +173,8 @@ describe('ERC1155Token', () => {
const tokenToTransfer = fungibleToken;
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
// create the expected error (a uint256 underflow)
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
spenderInitialFungibleBalance,
valueToTransfer,
);
@@ -349,8 +349,8 @@ describe('ERC1155Token', () => {
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
// create the expected error (a uint256 underflow)
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
spenderInitialFungibleBalance,
valuesToTransfer[0],
);

View File

@@ -139,8 +139,8 @@ blockchainTests('LibFillResults', env => {
takerAssetAmount: MAX_UINT256_ROOT,
});
const takerAssetFilledAmount = MAX_UINT256_ROOT;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.makerAssetAmount,
);
@@ -162,8 +162,8 @@ blockchainTests('LibFillResults', env => {
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
makerAssetFilledAmount,
order.makerFee,
);
@@ -180,8 +180,8 @@ blockchainTests('LibFillResults', env => {
takerFee: MAX_UINT256_ROOT.times(11),
});
const takerAssetFilledAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.takerFee,
);
@@ -303,8 +303,8 @@ blockchainTests('LibFillResults', env => {
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,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerAssetFilledAmount,
b.makerAssetFilledAmount,
);
@@ -314,8 +314,8 @@ blockchainTests('LibFillResults', env => {
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,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerAssetFilledAmount,
b.takerAssetFilledAmount,
);
@@ -325,8 +325,8 @@ blockchainTests('LibFillResults', env => {
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,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerFeePaid,
b.makerFeePaid,
);
@@ -336,8 +336,8 @@ blockchainTests('LibFillResults', env => {
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,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerFeePaid,
b.takerFeePaid,
);

View File

@@ -71,8 +71,8 @@ blockchainTests('LibMath', env => {
const numerator = ONE_ETHER;
const denominator = ZERO_AMOUNT;
const target = ONE_ETHER.times(0.01);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
numerator.times(target),
denominator,
);
@@ -85,8 +85,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -131,8 +131,8 @@ blockchainTests('LibMath', env => {
const denominator = ZERO_AMOUNT;
const target = ONE_ETHER.times(0.01);
// This will actually manifest as a subtraction underflow.
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
denominator,
new BigNumber(1),
);
@@ -145,8 +145,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -210,8 +210,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -275,8 +275,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -341,8 +341,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -407,8 +407,8 @@ blockchainTests('LibMath', env => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);

View File

@@ -35,8 +35,8 @@ describe('Reference Functions', () => {
it('reverts if computing `makerAssetFilledAmount` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.makerAssetFilledAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerAssetFilledAmount,
b.makerAssetFilledAmount,
);
@@ -46,8 +46,8 @@ describe('Reference Functions', () => {
it('reverts if computing `takerAssetFilledAmount` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.takerAssetFilledAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerAssetFilledAmount,
b.takerAssetFilledAmount,
);
@@ -57,8 +57,8 @@ describe('Reference Functions', () => {
it('reverts if computing `makerFeePaid` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.makerFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerFeePaid,
b.makerFeePaid,
);
@@ -68,8 +68,8 @@ describe('Reference Functions', () => {
it('reverts if computing `takerFeePaid` overflows', () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.takerFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerFeePaid,
b.takerFeePaid,
);
@@ -85,8 +85,8 @@ describe('Reference Functions', () => {
const numerator = ONE_ETHER;
const denominator = ZERO_AMOUNT;
const target = ONE_ETHER.times(0.01);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
numerator.times(target),
denominator,
);
@@ -99,8 +99,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -118,8 +118,8 @@ describe('Reference Functions', () => {
const denominator = ZERO_AMOUNT;
const target = ONE_ETHER.times(0.01);
// This will actually manifest as a subtraction underflow.
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
denominator,
new BigNumber(1),
);
@@ -132,8 +132,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -170,8 +170,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -208,8 +208,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -254,8 +254,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);
@@ -300,8 +300,8 @@ describe('Reference Functions', () => {
const numerator = MAX_UINT256;
const denominator = ONE_ETHER.dividedToIntegerBy(2);
const target = MAX_UINT256_ROOT.times(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
numerator,
target,
);

View File

@@ -77,8 +77,8 @@ blockchainTests('Exchange core internal functions', env => {
makerAssetAmount: constants.MAX_UINT256_ROOT,
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
leftOrder.makerAssetAmount,
rightOrder.makerAssetAmount,
);
@@ -94,8 +94,8 @@ blockchainTests('Exchange core internal functions', env => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: constants.MAX_UINT256_ROOT,
});
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
leftOrder.takerAssetAmount,
rightOrder.takerAssetAmount,
);
@@ -228,8 +228,8 @@ blockchainTests('Exchange core internal functions', env => {
makerFeePaid: constants.ZERO_AMOUNT,
takerFeePaid: constants.ZERO_AMOUNT,
};
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
orderTakerAssetFilledAmount,
takerAssetFillAmount,
);
@@ -357,8 +357,8 @@ blockchainTests('Exchange core internal functions', env => {
rightOrder.takerFeeAssetData = leftOrder.takerFeeAssetData;
// The expected error that should be thrown by the function.
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
matchedFillResults.left.takerFeePaid,
matchedFillResults.right.takerFeePaid,
);

View File

@@ -443,8 +443,8 @@ blockchainTests('Isolated fillOrder() tests', env => {
takerAssetAmount: MAX_UINT256_ROOT,
});
const takerAssetFillAmount = MAX_UINT256_ROOT;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFillAmount,
order.makerAssetAmount,
);
@@ -464,8 +464,8 @@ blockchainTests('Isolated fillOrder() tests', env => {
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
makerAssetFilledAmount,
order.makerFee,
);
@@ -480,8 +480,8 @@ blockchainTests('Isolated fillOrder() tests', env => {
takerFee: MAX_UINT256_ROOT.times(11),
});
const takerAssetFillAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFillAmount,
order.takerFee,
);

View File

@@ -39,8 +39,8 @@ describe('Reference functions', () => {
takerAssetAmount: MAX_UINT256_ROOT,
});
const takerAssetFilledAmount = MAX_UINT256_ROOT;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.makerAssetAmount,
);
@@ -60,8 +60,8 @@ describe('Reference functions', () => {
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
makerAssetFilledAmount,
order.makerFee,
);
@@ -76,8 +76,8 @@ describe('Reference functions', () => {
takerFee: MAX_UINT256_ROOT.times(11),
});
const takerAssetFilledAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.takerFee,
);

View File

@@ -718,8 +718,8 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
const signatures = _.times(COUNT, i => createOrderSignature(orders[i]));
orders[1].takerAssetAmount = MAX_UINT256;
const takerAssetFillAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
orders[0].takerAssetAmount,
orders[1].takerAssetAmount,
);
@@ -1031,8 +1031,8 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
const orders = [randomOrder({ takerAssetAmount: MAX_UINT256 })];
const signatures = _.times(orders.length, i => createOrderSignature(orders[i]));
const makerAssetFillAmount = new BigNumber(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
orders[0].takerAssetAmount,
makerAssetFillAmount,
);
@@ -1044,8 +1044,8 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
const orders = [randomOrder({ makerAssetAmount: constants.ZERO_AMOUNT })];
const signatures = _.times(orders.length, i => createOrderSignature(orders[i]));
const makerAssetFillAmount = ONE_ETHER;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
orders[0].takerAssetAmount.times(makerAssetFillAmount),
orders[0].makerAssetAmount,
);
@@ -1066,8 +1066,8 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
];
const signatures = _.times(orders.length, i => createOrderSignature(orders[i]));
const makerAssetFillAmount = new BigNumber(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
orders[0].makerAssetAmount,
orders[1].makerAssetAmount,
);

View File

@@ -41,7 +41,7 @@ If a vulnerability is discovered in the staking contract, operations may be halt
In this worst-case scenario, state has been irreperably corrupted and the staking contracts must be re-deployed. Users withdraw their funds from the vaults and re-stake under the new system, at will.
4. Vaults enter "Catostrophic Failure Mode" allowing users to withdraw their ZRX and Rewards.
4. Vaults enter "Catastrophic Failure Mode" allowing users to withdraw their ZRX and Rewards.
5. A Balance Oracle is deployed for determining the Reward balance of each user. (\*)
![](images/architecture_failure_mode.png)

View File

@@ -18,8 +18,10 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibFeeMath.sol";
import "../libs/LibStakingRichErrors.sol";
import "../immutable/MixinStorage.sol";
import "../immutable/MixinConstants.sol";
import "../interfaces/IStakingEvents.sol";
@@ -33,7 +35,7 @@ import "./MixinExchangeManager.sol";
/// @dev This mixin contains the logic for 0x protocol fees.
/// Protocol fees are sent by 0x exchanges every time there is a trade.
/// If the maker has associated their address with a pool (see MixinStakingPool.sol), then
/// If the maker has associated their address with a pool (see MixinStakingPool.sol), then
/// the fee will be attributed to their pool. At the end of an epoch the maker and
/// their pool will receive a rebate that is proportional to (i) the fee volume attributed
/// to their pool over the epoch, and (ii) the amount of stake provided by the maker and
@@ -121,7 +123,7 @@ contract MixinExchangeFees is
/// @dev Pays rewards to market making pools that were active this epoch.
/// Each pool receives a portion of the fees generated this epoch (see LibFeeMath) that is
/// proportional to (i) the fee volume attributed to their pool over the epoch, and
/// proportional to (i) the fee volume attributed to their pool over the epoch, and
/// (ii) the amount of stake provided by the maker and their delegators. Rebates are paid
/// into the Reward Vault (see MixinStakingPoolRewardVault) where they can be withdraw by makers and
/// the members of their pool. There will be a small amount of ETH leftover in this contract
@@ -223,10 +225,14 @@ contract MixinExchangeFees is
activePoolsThisEpoch.length = 0;
// step 3/3 send total payout to vault
require(
totalRewardsPaid <= initialContractBalance,
"MISCALCULATED_REWARDS"
);
// Sanity check rewards calculation
if (totalRewardsPaid > initialContractBalance) {
LibRichErrors.rrevert(LibStakingRichErrors.MiscalculatedRewardsError(
totalRewardsPaid,
initialContractBalance
));
}
if (totalRewardsPaid > 0) {
_depositIntoStakingPoolRewardVault(totalRewardsPaid);
}

View File

@@ -18,6 +18,8 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../interfaces/IStakingEvents.sol";
import "../immutable/MixinConstants.sol";
import "../immutable/MixinStorage.sol";
@@ -38,10 +40,11 @@ contract MixinExchangeManager is
/// @dev Asserts that the call is coming from a valid exchange.
modifier onlyExchange() {
require(
isValidExchangeAddress(msg.sender),
"ONLY_CALLABLE_BY_EXCHANGE"
);
if (!isValidExchangeAddress(msg.sender)) {
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableByExchangeError(
msg.sender
));
}
_;
}
@@ -51,10 +54,11 @@ contract MixinExchangeManager is
external
onlyOwner
{
require(
!validExchanges[addr],
"EXCHANGE_ADDRESS_ALREADY_REGISTERED"
);
if (validExchanges[addr]) {
LibRichErrors.rrevert(LibStakingRichErrors.ExchangeAddressAlreadyRegisteredError(
addr
));
}
validExchanges[addr] = true;
emit ExchangeAdded(addr);
}
@@ -65,10 +69,11 @@ contract MixinExchangeManager is
external
onlyOwner
{
require(
validExchanges[addr],
"EXCHANGE_ADDRESS_NOT_REGISTERED"
);
if (!validExchanges[addr]) {
LibRichErrors.rrevert(LibStakingRichErrors.ExchangeAddressNotRegisteredError(
addr
));
}
validExchanges[addr] = false;
emit ExchangeRemoved(addr);
}

View File

@@ -22,7 +22,7 @@ pragma solidity ^0.5.9;
/// @dev This vault manages staking pool rewards.
/// Rewards can be deposited and withdraw by the staking contract.
/// There is a "Catastrophic Failure Mode" that, when invoked, only
/// allows withdrawals to be made. Once this vault is in catostrophic
/// allows withdrawals to be made. Once this vault is in catastrophic
/// failure mode, it cannot be returned to normal mode; this prevents
/// corruption of related state in the staking contract.
interface IStakingPoolRewardVault {

View File

@@ -27,7 +27,7 @@ pragma solidity ^0.5.9;
/// Vaults should only be set to Catastrophic Failure Mode iff there is
/// non-recoverable corruption of the staking contracts. If there is a
/// recoverable flaw/bug/vulnerability, simply detach the staking contract
/// by setting its address to `address(0)`. Once in Catostrophic Failure Mode,
/// by setting its address to `address(0)`. Once in Catastrophic Failure Mode,
/// a vault cannot be reset to normal mode; this prevents corruption of related
/// state in the staking contract.
interface IVaultCore {
@@ -38,9 +38,9 @@ interface IVaultCore {
address stakingContractAddress
);
/// @dev Emitted when the Staking contract is put into Catostrophic Failure Mode
/// @dev Emitted when the Staking contract is put into Catastrophic Failure Mode
/// @param sender Address of sender (`msg.sender`)
event InCatostrophicFailureMode(
event InCatastrophicFailureMode(
address sender
);
@@ -50,9 +50,9 @@ interface IVaultCore {
function setStakingContract(address payable _stakingContractAddress)
external;
/// @dev Vault enters into Catostrophic Failure Mode.
/// @dev Vault enters into Catastrophic Failure Mode.
/// *** WARNING - ONCE IN CATOSTROPHIC FAILURE MODE, YOU CAN NEVER GO BACK! ***
/// Note that only the contract owner can call this function.
function enterCatostrophicFailure()
function enterCatastrophicFailure()
external;
}

View File

@@ -23,7 +23,7 @@ pragma solidity ^0.5.9;
/// When a user mints stake, their Zrx Tokens are deposited into this vault.
/// Similarly, when they burn stake, their Zrx Tokens are withdrawn from this vault.
/// There is a "Catastrophic Failure Mode" that, when invoked, only
/// allows withdrawals to be made. Once this vault is in catostrophic
/// allows withdrawals to be made. Once this vault is in catastrophic
/// failure mode, it cannot be returned to normal mode; this prevents
/// corruption of related state in the staking contract.
interface IZrxVault {
@@ -62,21 +62,21 @@ interface IZrxVault {
/// @dev Sets the ERC20 proxy.
/// Note that only the contract owner can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param erc20ProxyAddress Address of the 0x ERC20 Proxy.
function setErc20Proxy(address erc20ProxyAddress)
external;
/// @dev Sets the Zrx Asset Data.
/// Note that only the contract owner can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param _zrxAssetData Zrx asset data for the ERC20 Proxy.
function setZrxAssetData(bytes calldata _zrxAssetData)
external;
/// @dev Deposit an `amount` of Zrx Tokens from `owner` into the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
/// @param amount of Zrx Tokens to deposit.
function depositFrom(address owner, uint256 amount)
@@ -84,14 +84,14 @@ interface IZrxVault {
/// @dev Withdraw an `amount` of Zrx Tokens to `owner` from the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
/// @param amount of Zrx Tokens to withdraw.
function withdrawFrom(address owner, uint256 amount)
external;
/// @dev Withdraw ALL Zrx Tokens to `owner` from the vault.
/// Note that this can only be called when *in* Catostrophic Failure mode.
/// Note that this can only be called when *in* Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
function withdrawAllFrom(address owner)
external

View File

@@ -18,27 +18,42 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMathRichErrors.sol";
library LibSafeMath {
uint256 constant internal MAX_UINT_96 = 79228162514264337593543950335; // 2**96-1
uint256 constant internal MAX_UINT_64 = 18446744073709551615; // 2**64-1
/// @dev Returns the addition of two unsigned integers, reverting on overflow.
/// Note that this reverts on overflow.
function _add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "OVERFLOW");
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));
}
return c;
}
/// @dev Returns the subtraction of two unsigned integers.
/// Note that this reverts on underflow.
function _sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "UNDEROVERFLOW");
uint256 c = a - b;
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
}
uint256 c = a - b;
return c;
}
@@ -53,7 +68,13 @@ library LibSafeMath {
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
}
return c;
}
@@ -61,7 +82,14 @@ library LibSafeMath {
/// @dev Returns the integer division of two unsigned integers.
/// Note that this reverts on division by zero. The result is rounded towards zero.
function _div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "DIVISION_BY_ZERO");
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
}
uint256 c = a / b;
return c;
}
@@ -73,10 +101,12 @@ library LibSafeMath {
pure
returns (uint96)
{
require(
a <= MAX_UINT_96,
"VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96"
);
if (a > MAX_UINT_96) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256DowncastError(
LibSafeMathRichErrors.DowncastErrorCodes.VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96,
a
));
}
return uint96(a);
}
@@ -87,10 +117,12 @@ library LibSafeMath {
pure
returns (uint64)
{
require(
a <= MAX_UINT_64,
"VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64"
);
if (a > MAX_UINT_64) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256DowncastError(
LibSafeMathRichErrors.DowncastErrorCodes.VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
a
));
}
return uint64(a);
}
}

View File

@@ -18,6 +18,9 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMathRichErrors.sol";
library LibSafeMath64 {
@@ -25,16 +28,29 @@ library LibSafeMath64 {
/// Note that this reverts on overflow.
function _add(uint64 a, uint64 b) internal pure returns (uint64) {
uint64 c = a + b;
require(c >= a, "OVERFLOW");
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint64BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));
}
return c;
}
/// @dev Returns the subtraction of two unsigned integers.
/// Note that this reverts on underflow.
function _sub(uint64 a, uint64 b) internal pure returns (uint64) {
require(b <= a, "UNDEROVERFLOW");
uint64 c = a - b;
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint64BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
}
uint64 c = a - b;
return c;
}
@@ -49,7 +65,13 @@ library LibSafeMath64 {
}
uint64 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint64BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
}
return c;
}
@@ -57,7 +79,14 @@ library LibSafeMath64 {
/// @dev Returns the integer division of two unsigned integers.
/// Note that this reverts on division by zero. The result is rounded towards zero.
function _div(uint64 a, uint64 b) internal pure returns (uint64) {
require(b > 0, "DIVISION_BY_ZERO");
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint64BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
}
uint64 c = a / b;
return c;
}

View File

@@ -18,6 +18,9 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMathRichErrors.sol";
library LibSafeMath96 {
@@ -25,16 +28,29 @@ library LibSafeMath96 {
/// Note that this reverts on overflow.
function _add(uint96 a, uint96 b) internal pure returns (uint96) {
uint96 c = a + b;
require(c >= a, "OVERFLOW");
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint96BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));
}
return c;
}
/// @dev Returns the subtraction of two unsigned integers.
/// Note that this reverts on underflow.
function _sub(uint96 a, uint96 b) internal pure returns (uint96) {
require(b <= a, "UNDEROVERFLOW");
uint96 c = a - b;
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint96BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
}
uint96 c = a - b;
return c;
}
@@ -49,7 +65,13 @@ library LibSafeMath96 {
}
uint96 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint96BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
}
return c;
}
@@ -57,7 +79,14 @@ library LibSafeMath96 {
/// @dev Returns the integer division of two unsigned integers.
/// Note that this reverts on division by zero. The result is rounded towards zero.
function _div(uint96 a, uint96 b) internal pure returns (uint96) {
require(b > 0, "DIVISION_BY_ZERO");
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint96BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
}
uint96 c = a / b;
return c;
}

View File

@@ -14,12 +14,14 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../interfaces/IStructs.sol";
import "../interfaces/IWallet.sol";
library LibSignatureValidator {
using LibBytes for bytes;
// bytes4(keccak256("isValidSignature(bytes,bytes)")
@@ -39,19 +41,19 @@ library LibSignatureValidator {
view
returns (bool isValid)
{
require(
signature.length > 0,
"LENGTH_GREATER_THAN_0_REQUIRED"
);
if (signature.length == 0) {
LibRichErrors.rrevert(LibStakingRichErrors.SignatureLengthGreaterThan0RequiredError());
}
// Pop last byte off of signature byte array.
uint8 signatureTypeRaw = uint8(signature.popLastByte());
// Ensure signature is supported
require(
signatureTypeRaw < uint8(IStructs.SignatureType.NSignatureTypes),
"SIGNATURE_UNSUPPORTED"
);
if (signatureTypeRaw >= uint8(IStructs.SignatureType.NSignatureTypes)) {
LibRichErrors.rrevert(LibStakingRichErrors.SignatureUnsupportedError(
signature
));
}
IStructs.SignatureType signatureType = IStructs.SignatureType(signatureTypeRaw);
@@ -67,26 +69,30 @@ library LibSignatureValidator {
// it an explicit option. This aids testing and analysis. It is
// also the initialization value for the enum type.
if (signatureType == IStructs.SignatureType.Illegal) {
revert("SIGNATURE_ILLEGAL");
LibRichErrors.rrevert(LibStakingRichErrors.SignatureIllegalError(
signature
));
// Always invalid signature.
// Like Illegal, this is always implicitly available and therefore
// offered explicitly. It can be implicitly created by providing
// a correctly formatted but incorrect signature.
} else if (signatureType == IStructs.SignatureType.Invalid) {
require(
signature.length == 0,
"LENGTH_0_REQUIRED"
);
if (signature.length > 0) {
LibRichErrors.rrevert(LibStakingRichErrors.SignatureLength0RequiredError(
signature
));
}
isValid = false;
return isValid;
// Signature using EIP712
} else if (signatureType == IStructs.SignatureType.EIP712) {
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
if (signature.length != 65) {
LibRichErrors.rrevert(LibStakingRichErrors.SignatureLength65RequiredError(
signature
));
}
v = uint8(signature[0]);
r = signature.readBytes32(1);
s = signature.readBytes32(33);
@@ -101,10 +107,11 @@ library LibSignatureValidator {
// Signed using web3.eth_sign
} else if (signatureType == IStructs.SignatureType.EthSign) {
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
if (signature.length != 65) {
LibRichErrors.rrevert(LibStakingRichErrors.SignatureLength65RequiredError(
signature
));
}
v = uint8(signature[0]);
r = signature.readBytes32(1);
s = signature.readBytes32(33);
@@ -136,7 +143,9 @@ library LibSignatureValidator {
// that we currently support. In this case returning false
// may lead the caller to incorrectly believe that the
// signature was invalid.)
revert("SIGNATURE_UNSUPPORTED");
LibRichErrors.rrevert(LibStakingRichErrors.SignatureUnsupportedError(
signature
));
}
/// @dev Verifies signature using logic defined by Wallet contract.
@@ -169,10 +178,12 @@ library LibSignatureValidator {
(bool success, bytes memory result) = walletAddress.staticcall(callData);
// Sanity check call and extract the magic value
require(
success,
"WALLET_ERROR"
);
if (!success) {
LibRichErrors.rrevert(LibStakingRichErrors.WalletError(
walletAddress,
result
));
}
bytes4 magicValue = result.readBytes4(0);
isValid = (magicValue == EIP1271_MAGIC_VALUE);

View File

@@ -0,0 +1,446 @@
/*
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 LibStakingRichErrors {
// bytes4(keccak256("MiscalculatedRewardsError(uint256,uint256)"))
bytes4 internal constant MISCALCULATED_REWARDS_ERROR_SELECTOR =
0xf7806c4e;
// bytes4(keccak256("OnlyCallableByExchangeError(address)"))
bytes4 internal constant ONLY_CALLABLE_BY_EXCHANGE_ERROR_SELECTOR =
0xb56d2df0;
// bytes4(keccak256("ExchangeAddressAlreadyRegisteredError(address)"))
bytes4 internal constant EXCHANGE_ADDRESS_ALREADY_REGISTERED_ERROR_SELECTOR =
0xc87a78b7;
// bytes4(keccak256("ExchangeAddressNotRegisteredError(address)"))
bytes4 internal constant EXCHANGE_ADDRESS_NOT_REGISTERED_ERROR_SELECTOR =
0x7dc025b0;
// bytes4(keccak256("SignatureLengthGreaterThan0RequiredError()"))
bytes internal constant SIGNATURE_LENGTH_GREATER_THAN_0_REQUIRED_ERROR =
hex"2dcb01d9";
// bytes4(keccak256("SignatureUnsupportedError(bytes)"))
bytes4 internal constant SIGNATURE_UNSUPPORTED_ERROR_SELECTOR =
0xffca2a70;
// bytes4(keccak256("SignatureIllegalError(bytes)"))
bytes4 internal constant SIGNATURE_ILLEGAL_ERROR_SELECTOR =
0x4a95093c;
// bytes4(keccak256("SignatureLength0RequiredError(bytes)"))
bytes4 internal constant SIGNATURE_LENGTH_0_REQUIRED_ERROR_SELECTOR =
0xcbcd59a2;
// bytes4(keccak256("SignatureLength65RequiredError(bytes)"))
bytes4 internal constant SIGNATURE_LENGTH_65_REQUIRED_ERROR_SELECTOR =
0x091d7ab9;
// bytes4(keccak256("WalletError(address,bytes)"))
bytes4 internal constant WALLET_ERROR_SELECTOR =
0x0cfc935d;
// bytes4(keccak256("InsufficientBalanceError(uint256,uint256)"))
bytes4 internal constant INSUFFICIENT_BALANCE_ERROR_SELECTOR =
0x84c8b7c9;
// bytes4(keccak256("OnlyCallableByPoolOperatorError(address,address)"))
bytes4 internal constant ONLY_CALLABLE_BY_POOL_OPERATOR_ERROR_SELECTOR =
0x6cfa0c22;
// bytes4(keccak256("OnlyCallableByPoolOperatorOrMakerError(address,address,address)"))
bytes4 internal constant ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER_ERROR_SELECTOR =
0x7d9e1c10;
// bytes4(keccak256("InvalidMakerSignatureError(bytes32,address,bytes)"))
bytes4 internal constant INVALID_MAKER_SIGNATURE_ERROR_SELECTOR =
0x726b89c8;
// bytes4(keccak256("MakerAddressAlreadyRegisteredError(address)"))
bytes4 internal constant MAKER_ADDRESS_ALREADY_REGISTERED_ERROR_SELECTOR =
0x5a3971da;
// bytes4(keccak256("MakerAddressNotRegisteredError(address,bytes32,bytes32)"))
bytes4 internal constant MAKER_ADDRESS_NOT_REGISTERED_ERROR_SELECTOR =
0x12ab07e8;
// bytes4(keccak256("WithdrawAmountExceedsMemberBalanceError(uint256,uint256)"))
bytes4 internal constant WITHDRAW_AMOUNT_EXCEEDS_MEMBER_BALANCE_ERROR_SELECTOR =
0xfc9c065f;
// bytes4(keccak256("BlockTimestampTooLowError(uint64,uint64)"))
bytes4 internal constant BLOCK_TIMESTAMP_TOO_LOW_ERROR_SELECTOR =
0x887225f7;
// bytes4(keccak256("OnlyCallableByStakingContractError(address)"))
bytes4 internal constant ONLY_CALLABLE_BY_STAKING_CONTRACT_ERROR_SELECTOR =
0xca1d07a2;
// bytes4(keccak256("OnlyCallableIfInCatastrophicFailureError()"))
bytes internal constant ONLY_CALLABLE_IF_IN_CATASTROPHIC_FAILURE_ERROR =
hex"3ef081cc";
// bytes4(keccak256("OnlyCallableIfNotInCatastrophicFailureError()"))
bytes internal constant ONLY_CALLABLE_IF_NOT_IN_CATASTROPHIC_FAILURE_ERROR =
hex"7dd020ce";
// bytes4(keccak256("AmountExceedsBalanceOfPoolError(uint256,uint96)"))
bytes4 internal constant AMOUNT_EXCEEDS_BALANCE_OF_POOL_ERROR_SELECTOR =
0x4c5c09dd;
// bytes4(keccak256("OperatorShareMustBeBetween0And100Error(bytes32,uint8)"))
bytes4 internal constant OPERATOR_SHARE_MUST_BE_BETWEEN_0_AND_100_ERROR_SELECTOR =
0xde447684;
// bytes4(keccak256("PoolAlreadyExistsError(bytes32)"))
bytes4 internal constant POOL_ALREADY_EXISTS_ERROR_SELECTOR =
0x2a5e4dcf;
// solhint-disable func-name-mixedcase
function MiscalculatedRewardsError(
uint256 totalRewardsPaid,
uint256 initialContractBalance
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
MISCALCULATED_REWARDS_ERROR_SELECTOR,
totalRewardsPaid,
initialContractBalance
);
}
function OnlyCallableByExchangeError(
address senderAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ONLY_CALLABLE_BY_EXCHANGE_ERROR_SELECTOR,
senderAddress
);
}
function ExchangeAddressAlreadyRegisteredError(
address exchangeAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
EXCHANGE_ADDRESS_ALREADY_REGISTERED_ERROR_SELECTOR,
exchangeAddress
);
}
function ExchangeAddressNotRegisteredError(
address exchangeAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
EXCHANGE_ADDRESS_NOT_REGISTERED_ERROR_SELECTOR,
exchangeAddress
);
}
function SignatureLengthGreaterThan0RequiredError()
internal
pure
returns (bytes memory)
{
return SIGNATURE_LENGTH_GREATER_THAN_0_REQUIRED_ERROR;
}
function SignatureUnsupportedError(
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_UNSUPPORTED_ERROR_SELECTOR,
signature
);
}
function SignatureIllegalError(
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_ILLEGAL_ERROR_SELECTOR,
signature
);
}
function SignatureLength0RequiredError(
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_LENGTH_0_REQUIRED_ERROR_SELECTOR,
signature
);
}
function SignatureLength65RequiredError(
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_LENGTH_65_REQUIRED_ERROR_SELECTOR,
signature
);
}
function WalletError(
address walletAddress,
bytes memory errorData
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
WALLET_ERROR_SELECTOR,
walletAddress,
errorData
);
}
function InsufficientBalanceError(
uint256 amount,
uint256 balance
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INSUFFICIENT_BALANCE_ERROR_SELECTOR,
amount,
balance
);
}
function OnlyCallableByPoolOperatorError(
address senderAddress,
address poolOperatorAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ONLY_CALLABLE_BY_POOL_OPERATOR_ERROR_SELECTOR,
senderAddress,
poolOperatorAddress
);
}
function OnlyCallableByPoolOperatorOrMakerError(
address senderAddress,
address poolOperatorAddress,
address makerAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER_ERROR_SELECTOR,
senderAddress,
poolOperatorAddress,
makerAddress
);
}
function InvalidMakerSignatureError(
bytes32 poolId,
address makerAddress,
bytes memory makerSignature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
INVALID_MAKER_SIGNATURE_ERROR_SELECTOR,
poolId,
makerAddress,
makerSignature
);
}
function MakerAddressAlreadyRegisteredError(
address makerAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
MAKER_ADDRESS_ALREADY_REGISTERED_ERROR_SELECTOR,
makerAddress
);
}
function MakerAddressNotRegisteredError(
address makerAddress,
bytes32 makerPoolId,
bytes32 poolId
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
MAKER_ADDRESS_NOT_REGISTERED_ERROR_SELECTOR,
makerAddress,
makerPoolId,
poolId
);
}
function WithdrawAmountExceedsMemberBalanceError(
uint256 withdrawAmount,
uint256 balance
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
WITHDRAW_AMOUNT_EXCEEDS_MEMBER_BALANCE_ERROR_SELECTOR,
withdrawAmount,
balance
);
}
function BlockTimestampTooLowError(
uint64 epochEndTime,
uint64 currentBlockTimestamp
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
BLOCK_TIMESTAMP_TOO_LOW_ERROR_SELECTOR,
epochEndTime,
currentBlockTimestamp
);
}
function OnlyCallableByStakingContractError(
address senderAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
ONLY_CALLABLE_BY_STAKING_CONTRACT_ERROR_SELECTOR,
senderAddress
);
}
function OnlyCallableIfInCatastrophicFailureError()
internal
pure
returns (bytes memory)
{
return ONLY_CALLABLE_IF_IN_CATASTROPHIC_FAILURE_ERROR;
}
function OnlyCallableIfNotInCatastrophicFailureError()
internal
pure
returns (bytes memory)
{
return ONLY_CALLABLE_IF_NOT_IN_CATASTROPHIC_FAILURE_ERROR;
}
function AmountExceedsBalanceOfPoolError(
uint256 amount,
uint96 poolBalance
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
AMOUNT_EXCEEDS_BALANCE_OF_POOL_ERROR_SELECTOR,
amount,
poolBalance
);
}
function OperatorShareMustBeBetween0And100Error(
bytes32 poolId,
uint8 poolOperatorShare
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
OPERATOR_SHARE_MUST_BE_BETWEEN_0_AND_100_ERROR_SELECTOR,
poolId,
poolOperatorShare
);
}
function PoolAlreadyExistsError(
bytes32 poolId
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
POOL_ALREADY_EXISTS_ERROR_SELECTOR,
poolId
);
}
}

View File

@@ -18,6 +18,8 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibRewardMath.sol";
import "../immutable/MixinConstants.sol";
@@ -62,7 +64,7 @@ import "./MixinTimeLockedStake.sol";
///
/// -- Valid State Transtions --
/// Activated -> Deactivated & TimeLocked
///
///
/// Activated & Delegated -> Deactivated & TimeLocked
///
/// Deactivated & TimeLocked -> Deactivated & Withdrawable
@@ -117,10 +119,13 @@ contract MixinStake is
{
address owner = msg.sender;
_syncTimeLockedStake(owner);
require(
amount <= getDeactivatedStake(owner),
"INSUFFICIENT_BALANCE"
);
if (amount > getDeactivatedStake(owner)) {
LibRichErrors.rrevert(LibStakingRichErrors.InsufficientBalanceError(
amount,
getDeactivatedStake(owner)
));
}
_burnStake(owner, amount);
}
@@ -131,10 +136,13 @@ contract MixinStake is
{
address owner = msg.sender;
_syncTimeLockedStake(owner);
require(
amount <= getActivatableStake(owner),
"INSUFFICIENT_BALANCE"
);
if (amount > getActivatableStake(owner)) {
LibRichErrors.rrevert(LibStakingRichErrors.InsufficientBalanceError(
amount,
getActivatableStake(owner)
));
}
activatedStakeByOwner[owner] = activatedStakeByOwner[owner]._add(amount);
totalActivatedStake = totalActivatedStake._add(amount);
}
@@ -146,10 +154,13 @@ contract MixinStake is
{
address owner = msg.sender;
_syncTimeLockedStake(owner);
require(
amount <= getActivatedStake(owner),
"INSUFFICIENT_BALANCE"
);
if (amount > getActivatedStake(owner)) {
LibRichErrors.rrevert(LibStakingRichErrors.InsufficientBalanceError(
amount,
getActivatedStake(owner)
));
}
activatedStakeByOwner[owner] = activatedStakeByOwner[owner]._sub(amount);
totalActivatedStake = totalActivatedStake._sub(amount);
_timeLockStake(owner, amount);

View File

@@ -19,6 +19,8 @@
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibSignatureValidator.sol";
import "../libs/LibEIP712Hash.sol";
@@ -32,7 +34,7 @@ import "./MixinStakingPoolRewardVault.sol";
/// @dev This mixin contains logic for staking pools.
/// A pool has a single operator and any number of delegators (members).
/// Any staker can create a pool, although at present it is only beneficial
/// for market makers to create staking pools. A market maker *must* create a
/// for market makers to create staking pools. A market maker *must* create a
/// pool in order to receive fee-based rewards at the end of each epoch (see MixinExchangeFees).
/// Moreover, creating a staking pool leverages the delegated stake within the pool,
/// which is counted towards a maker's total stake when computing rewards. A market maker
@@ -66,10 +68,13 @@ contract MixinStakingPool is
/// @dev Asserts that the sender is the operator of the input pool.
/// @param poolId Pool sender must be operator of.
modifier onlyStakingPoolOperator(bytes32 poolId) {
require(
msg.sender == getStakingPoolOperator(poolId),
"ONLY_CALLABLE_BY_POOL_OPERATOR"
);
address poolOperator = getStakingPoolOperator(poolId);
if (msg.sender != poolOperator) {
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableByPoolOperatorError(
msg.sender,
poolOperator
));
}
_;
}
@@ -78,10 +83,16 @@ contract MixinStakingPool is
/// @param poolId Pool sender must be operator of.
/// @param makerAddress Address of a maker in the pool.
modifier onlyStakingPoolOperatorOrMaker(bytes32 poolId, address makerAddress) {
require(
msg.sender == getStakingPoolOperator(poolId) || msg.sender == makerAddress,
"ONLY_CALLABLE_BY_POOL_OPERATOR_OR_MAKER"
);
address poolOperator = getStakingPoolOperator(poolId);
if (msg.sender != poolOperator && msg.sender != makerAddress) {
LibRichErrors.rrevert(
LibStakingRichErrors.OnlyCallableByPoolOperatorOrMakerError(
msg.sender,
poolOperator,
makerAddress
)
);
}
_;
}
@@ -129,14 +140,19 @@ contract MixinStakingPool is
onlyStakingPoolOperator(poolId)
{
// sanity check - did maker agree to join this pool?
require(
isValidMakerSignature(poolId, makerAddress, makerSignature),
"INVALID_MAKER_SIGNATURE"
);
require(
!isMakerAssignedToStakingPool(makerAddress),
"MAKER_ADDRESS_ALREADY_REGISTERED"
);
if (!isValidMakerSignature(poolId, makerAddress, makerSignature)) {
LibRichErrors.rrevert(LibStakingRichErrors.InvalidMakerSignatureError(
poolId,
makerAddress,
makerSignature
));
}
if (isMakerAssignedToStakingPool(makerAddress)) {
LibRichErrors.rrevert(LibStakingRichErrors.MakerAddressAlreadyRegisteredError(
makerAddress
));
}
poolIdByMakerAddress[makerAddress] = poolId;
makerAddressesByPoolId[poolId].push(makerAddress);
@@ -159,10 +175,14 @@ contract MixinStakingPool is
external
onlyStakingPoolOperatorOrMaker(poolId, makerAddress)
{
require(
getStakingPoolIdOfMaker(makerAddress) == poolId,
"MAKER_ADDRESS_NOT_REGISTERED"
);
bytes32 makerPoolId = getStakingPoolIdOfMaker(makerAddress);
if (makerPoolId != poolId) {
LibRichErrors.rrevert(LibStakingRichErrors.MakerAddressNotRegisteredError(
makerAddress,
makerPoolId,
poolId
));
}
// load list of makers for the input pool.
address[] storage makerAddressesByPoolIdPtr = makerAddressesByPoolId[poolId];

View File

@@ -18,6 +18,8 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibRewardMath.sol";
import "../immutable/MixinStorage.sol";
@@ -39,7 +41,7 @@ import "./MixinStakingPool.sol";
/// is currently at the end of each epoch. Additionally, each member has an associated "Shadow Balance" which is updated only
/// when a member delegates/undelegates stake to the pool, along with a "Total Shadow Balance" that represents the cumulative
/// Shadow Balances of all members in a pool.
///
///
/// -- Member Balances --
/// Terminology:
/// Real Balance - The reward balance in ETH of a member.
@@ -52,11 +54,11 @@ import "./MixinStakingPool.sol";
/// member delegates stake, we *increase* their Shadow Balance and the Total Shadow Balance of the pool.
///
/// 2. When a member withdraws a portion of their reward, their realized balance increases but their ownership
/// within the pool remains unchanged. Thus, we simultaneously *decrease* their Real Balance and
/// within the pool remains unchanged. Thus, we simultaneously *decrease* their Real Balance and
/// *increase* their Shadow Balance by the amount withdrawn. The cumulative balance decrease and increase, respectively.
///
/// 3. When a member undelegates, the portion of their reward that corresponds to that stake is also withdrawn. Thus,
/// their realized balance *increases* while their ownership of the pool *decreases*. To reflect this, we
/// their realized balance *increases* while their ownership of the pool *decreases*. To reflect this, we
/// decrease their Shadow Balance, the Total Shadow Balance, their Real Balance, and the Total Real Balance.
contract MixinStakingPoolRewards is
IStakingEvents,
@@ -108,10 +110,12 @@ contract MixinStakingPoolRewards is
// sanity checks
address payable member = msg.sender;
uint256 memberBalance = computeRewardBalanceOfStakingPoolMember(poolId, member);
require(
amount <= memberBalance,
"INVALID_AMOUNT"
);
if (amount > memberBalance) {
LibRichErrors.rrevert(LibStakingRichErrors.WithdrawAmountExceedsMemberBalanceError(
amount,
memberBalance
));
}
// update shadow rewards
shadowRewardsInPoolByOwner[member][poolId] = shadowRewardsInPoolByOwner[member][poolId]._add(amount);
@@ -178,7 +182,7 @@ contract MixinStakingPoolRewards is
/// @dev Returns the shadow balance of a specific member of a staking pool.
/// @param poolId Unique id of pool.
/// @param member The member of the pool.
/// @param member The member of the pool.
/// @return Balance.
function getShadowBalanceOfStakingPoolMember(bytes32 poolId, address member)
public
@@ -201,7 +205,7 @@ contract MixinStakingPoolRewards is
/// @dev Computes the reward balance in ETH of a specific member of a pool.
/// @param poolId Unique id of pool.
/// @param member The member of the pool.
/// @param member The member of the pool.
/// @return Balance.
function computeRewardBalanceOfStakingPoolMember(bytes32 poolId, address member)
public
@@ -223,7 +227,7 @@ contract MixinStakingPoolRewards is
/// with the total shadow balance of the pool. This ensures that
/// any rewards belonging to existing members will not be diluted.
/// @param poolId Unique Id of pool to join.
/// @param member The member to join.
/// @param member The member to join.
/// @param amountOfStakeToDelegate The stake to be delegated by `member` upon joining.
/// @param totalStakeDelegatedToPool The amount of stake currently delegated to the pool.
/// This does not include `amountOfStakeToDelegate`.
@@ -256,7 +260,7 @@ contract MixinStakingPoolRewards is
/// with the total shadow balance of the pool. This ensures that
/// any rewards belonging to co-members will not be inflated.
/// @param poolId Unique Id of pool to leave.
/// @param member The member to leave.
/// @param member The member to leave.
/// @param amountOfStakeToUndelegate The stake to be undelegated by `member` upon leaving.
/// @param totalStakeDelegatedToPoolByMember The amount of stake currently delegated to the pool by the member.
/// This includes `amountOfStakeToUndelegate`.

View File

@@ -18,6 +18,8 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibSafeMath64.sol";
import "../immutable/MixinConstants.sol";
@@ -75,7 +77,7 @@ contract MixinScheduler is
}
/// @dev Returns the earliest end time in seconds of this epoch.
/// The next epoch can begin once this time is reached.
/// The next epoch can begin once this time is reached.
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
/// @return Time in seconds.
function getCurrentEpochEarliestEndTimeInSeconds()
@@ -140,17 +142,20 @@ contract MixinScheduler is
uint64 currentBlockTimestamp = block.timestamp._downcastToUint64();
// validate that we can increment the current epoch
require(
getCurrentEpochEarliestEndTimeInSeconds() <= currentBlockTimestamp,
"BLOCK_TIMESTAMP_TOO_LOW"
);
uint64 epochEndTime = getCurrentEpochEarliestEndTimeInSeconds();
if (epochEndTime > currentBlockTimestamp) {
LibRichErrors.rrevert(LibStakingRichErrors.BlockTimestampTooLowError(
epochEndTime,
currentBlockTimestamp
));
}
// incremment epoch
uint64 nextEpoch = currentEpoch._add(1);
currentEpoch = nextEpoch;
currentEpochStartTimeInSeconds = currentBlockTimestamp;
uint64 earliestEndTimeInSeconds = currentEpochStartTimeInSeconds._add(getEpochDurationInSeconds());
// notify of epoch change
emit EpochChanged(
currentEpoch,
@@ -163,7 +168,7 @@ contract MixinScheduler is
currentTimeLockPeriod = currentTimeLockPeriod._add(1);
currentTimeLockPeriodStartEpoch = currentEpoch;
uint64 endEpoch = currentEpoch._add(getTimeLockDurationInEpochs());
// notify
emit TimeLockPeriodChanged(
currentTimeLockPeriod,

View File

@@ -19,6 +19,8 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibStakingRichErrors.sol";
import "../interfaces/IVaultCore.sol";
@@ -30,7 +32,7 @@ import "../interfaces/IVaultCore.sol";
/// Vaults should only be set to Catastrophic Failure Mode iff there is
/// non-recoverable corruption of the staking contracts. If there is a
/// recoverable flaw/bug/vulnerability, simply detach the staking contract
/// by setting its address to `address(0)`. Once in Catostrophic Failure Mode,
/// by setting its address to `address(0)`. Once in Catastrophic Failure Mode,
/// a vault cannot be reset to normal mode; this prevents corruption of related
/// state in the staking contract.
contract MixinVaultCore is
@@ -41,39 +43,38 @@ contract MixinVaultCore is
// Address of staking contract
address payable internal stakingContractAddress;
// True iff vault has been set to Catostrophic Failure Mode
bool internal isInCatostrophicFailure;
// True iff vault has been set to Catastrophic Failure Mode
bool internal isInCatastrophicFailure;
/// @dev Constructor.
constructor() public {
stakingContractAddress = 0x0000000000000000000000000000000000000000;
isInCatostrophicFailure = false;
isInCatastrophicFailure = false;
}
/// @dev Asserts that the sender (`msg.sender`) is the staking contract.
modifier onlyStakingContract {
require(
msg.sender == stakingContractAddress,
"ONLY_CALLABLE_BY_STAKING_CONTRACT"
);
if (msg.sender != stakingContractAddress) {
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableByStakingContractError(
msg.sender
));
}
_;
}
/// @dev Asserts that this contract *is in* Catostrophic Failure Mode.
modifier onlyInCatostrophicFailure {
require(
isInCatostrophicFailure,
"ONLY_CALLABLE_IN_CATOSTROPHIC_FAILURE"
);
/// @dev Asserts that this contract *is in* Catastrophic Failure Mode.
modifier onlyInCatastrophicFailure {
if (!isInCatastrophicFailure) {
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableIfInCatastrophicFailureError());
}
_;
}
/// @dev Asserts that this contract *is not in* Catostrophic Failure Mode.
modifier onlyNotInCatostrophicFailure {
require(
!isInCatostrophicFailure,
"ONLY_CALLABLE_NOT_IN_CATOSTROPHIC_FAILURE"
);
/// @dev Asserts that this contract *is not in* Catastrophic Failure Mode.
modifier onlyNotInCatastrophicFailure {
if (isInCatastrophicFailure) {
LibRichErrors.rrevert(LibStakingRichErrors.OnlyCallableIfNotInCatastrophicFailureError());
}
_;
}
@@ -88,14 +89,14 @@ contract MixinVaultCore is
emit StakingContractChanged(stakingContractAddress);
}
/// @dev Vault enters into Catostrophic Failure Mode.
/// @dev Vault enters into Catastrophic Failure Mode.
/// *** WARNING - ONCE IN CATOSTROPHIC FAILURE MODE, YOU CAN NEVER GO BACK! ***
/// Note that only the contract owner can call this function.
function enterCatostrophicFailure()
function enterCatastrophicFailure()
external
onlyOwner
{
isInCatostrophicFailure = true;
emit InCatostrophicFailureMode(msg.sender);
isInCatastrophicFailure = true;
emit InCatastrophicFailureMode(msg.sender);
}
}

View File

@@ -18,8 +18,10 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../libs/LibSafeMath.sol";
import "../libs/LibSafeMath96.sol";
import "../libs/LibStakingRichErrors.sol";
import "./MixinVaultCore.sol";
import "../interfaces/IStakingPoolRewardVault.sol";
import "../immutable/MixinConstants.sol";
@@ -28,7 +30,7 @@ import "../immutable/MixinConstants.sol";
/// @dev This vault manages staking pool rewards.
/// Rewards can be deposited and withdraw by the staking contract.
/// There is a "Catastrophic Failure Mode" that, when invoked, only
/// allows withdrawals to be made. Once this vault is in catostrophic
/// allows withdrawals to be made. Once this vault is in catastrophic
/// failure mode, it cannot be returned to normal mode; this prevents
/// corruption of related state in the staking contract.
///
@@ -53,7 +55,7 @@ contract StakingPoolRewardVault is
external
payable
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
emit RewardDeposited(UNKNOWN_STAKING_POOL_ID, msg.value);
}
@@ -66,7 +68,7 @@ contract StakingPoolRewardVault is
external
payable
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
// update balance of pool
uint256 amount = msg.value;
@@ -87,7 +89,7 @@ contract StakingPoolRewardVault is
function recordDepositFor(bytes32 poolId, uint256 amount)
external
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
// update balance of pool
Balance memory balance = balanceByPoolId[poolId];
@@ -105,11 +107,13 @@ contract StakingPoolRewardVault is
onlyStakingContract
{
// sanity check - sufficient balance?
require(
amount <= balanceByPoolId[poolId].operatorBalance,
"AMOUNT_EXCEEDS_BALANCE_OF_POOL"
);
if (amount > balanceByPoolId[poolId].operatorBalance) {
LibRichErrors.rrevert(LibStakingRichErrors.AmountExceedsBalanceOfPoolError(
amount,
balanceByPoolId[poolId].operatorBalance
));
}
// update balance and transfer `amount` in ETH to staking contract
balanceByPoolId[poolId].operatorBalance -= amount._downcastToUint96();
stakingContractAddress.transfer(amount);
@@ -128,10 +132,12 @@ contract StakingPoolRewardVault is
onlyStakingContract
{
// sanity check - sufficient balance?
require(
amount <= balanceByPoolId[poolId].membersBalance,
"AMOUNT_EXCEEDS_BALANCE_OF_POOL"
);
if (amount > balanceByPoolId[poolId].membersBalance) {
LibRichErrors.rrevert(LibStakingRichErrors.AmountExceedsBalanceOfPoolError(
amount,
balanceByPoolId[poolId].membersBalance
));
}
// update balance and transfer `amount` in ETH to staking contract
balanceByPoolId[poolId].membersBalance -= amount._downcastToUint96();
@@ -149,20 +155,23 @@ contract StakingPoolRewardVault is
function registerStakingPool(bytes32 poolId, uint8 poolOperatorShare)
external
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
// operator share must be a valid percentage
require(
poolOperatorShare <= 100,
"OPERATOR_SHARE_MUST_BE_BETWEEN_0_AND_100"
);
if (poolOperatorShare > 100) {
LibRichErrors.rrevert(LibStakingRichErrors.OperatorShareMustBeBetween0And100Error(
poolId,
poolOperatorShare
));
}
// pool must not exist
Balance memory balance = balanceByPoolId[poolId];
require(
!balance.initialized,
"POOL_ALREADY_EXISTS"
);
if (balance.initialized) {
LibRichErrors.rrevert(LibStakingRichErrors.PoolAlreadyExistsError(
poolId
));
}
// set initial balance
balance.initialized = true;

View File

@@ -29,7 +29,7 @@ import "./MixinVaultCore.sol";
/// When a user mints stake, their Zrx Tokens are deposited into this vault.
/// Similarly, when they burn stake, their Zrx Tokens are withdrawn from this vault.
/// There is a "Catastrophic Failure Mode" that, when invoked, only
/// allows withdrawals to be made. Once this vault is in catostrophic
/// allows withdrawals to be made. Once this vault is in catastrophic
/// failure mode, it cannot be returned to normal mode; this prevents
/// corruption of related state in the staking contract.
contract ZrxVault is
@@ -70,12 +70,12 @@ contract ZrxVault is
/// @dev Sets the ERC20 proxy.
/// Note that only the contract owner can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param erc20ProxyAddress Address of the 0x ERC20 Proxy.
function setErc20Proxy(address erc20ProxyAddress)
external
onlyOwner
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
erc20Proxy = IAssetProxy(erc20ProxyAddress);
emit Erc20ProxyChanged(erc20ProxyAddress);
@@ -83,12 +83,12 @@ contract ZrxVault is
/// @dev Sets the Zrx Asset Data.
/// Note that only the contract owner can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param _zrxAssetData Zrx asset data for the ERC20 Proxy.
function setZrxAssetData(bytes calldata _zrxAssetData)
external
onlyOwner
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
zrxAssetData = _zrxAssetData;
emit ZrxAssetDataChanged(_zrxAssetData);
@@ -96,13 +96,13 @@ contract ZrxVault is
/// @dev Deposit an `amount` of Zrx Tokens from `owner` into the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
/// @param amount of Zrx Tokens to deposit.
function depositFrom(address owner, uint256 amount)
external
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
// update balance
balances[owner] = balances[owner]._add(amount);
@@ -121,23 +121,23 @@ contract ZrxVault is
/// @dev Withdraw an `amount` of Zrx Tokens to `owner` from the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catostrophic Failure mode.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
/// @param amount of Zrx Tokens to withdraw.
function withdrawFrom(address owner, uint256 amount)
external
onlyStakingContract
onlyNotInCatostrophicFailure
onlyNotInCatastrophicFailure
{
_withdrawFrom(owner, amount);
}
/// @dev Withdraw ALL Zrx Tokens to `owner` from the vault.
/// Note that this can only be called when *in* Catostrophic Failure mode.
/// Note that this can only be called when *in* Catastrophic Failure mode.
/// @param owner of Zrx Tokens.
function withdrawAllFrom(address owner)
external
onlyInCatostrophicFailure
onlyInCatastrophicFailure
returns (uint256)
{
// get total balance

View File

@@ -36,7 +36,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingPoolRewardVault|IStakingProxy|IStructs|IVaultCore|IWallet|IZrxVault|LibEIP712Hash|LibFeeMath|LibFeeMathTest|LibRewardMath|LibSafeMath|LibSafeMath64|LibSafeMath96|LibSignatureValidator|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|LibFeeMath|LibFeeMathTest|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:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@@ -21,6 +21,7 @@ import * as LibSafeMath from '../generated-artifacts/LibSafeMath.json';
import * as LibSafeMath64 from '../generated-artifacts/LibSafeMath64.json';
import * as LibSafeMath96 from '../generated-artifacts/LibSafeMath96.json';
import * as LibSignatureValidator from '../generated-artifacts/LibSignatureValidator.json';
import * as LibStakingRichErrors from '../generated-artifacts/LibStakingRichErrors.json';
import * as MixinConstants from '../generated-artifacts/MixinConstants.json';
import * as MixinDelegatedStake from '../generated-artifacts/MixinDelegatedStake.json';
import * as MixinDeploymentConstants from '../generated-artifacts/MixinDeploymentConstants.json';
@@ -64,6 +65,7 @@ export const artifacts = {
LibSafeMath64: LibSafeMath64 as ContractArtifact,
LibSafeMath96: LibSafeMath96 as ContractArtifact,
LibSignatureValidator: LibSignatureValidator as ContractArtifact,
LibStakingRichErrors: LibStakingRichErrors as ContractArtifact,
MixinDelegatedStake: MixinDelegatedStake as ContractArtifact,
MixinStake: MixinStake as ContractArtifact,
MixinStakeBalances: MixinStakeBalances as ContractArtifact,

View File

@@ -19,6 +19,7 @@ export * from '../generated-wrappers/lib_safe_math';
export * from '../generated-wrappers/lib_safe_math64';
export * from '../generated-wrappers/lib_safe_math96';
export * from '../generated-wrappers/lib_signature_validator';
export * from '../generated-wrappers/lib_staking_rich_errors';
export * from '../generated-wrappers/mixin_constants';
export * from '../generated-wrappers/mixin_delegated_stake';
export * from '../generated-wrappers/mixin_deployment_constants';

View File

@@ -1,7 +1,5 @@
import { expectTransactionFailedAsync } from '@0x/contracts-test-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { expect } from '@0x/contracts-test-utils';
import { BigNumber, RevertError } from '@0x/utils';
import * as _ from 'lodash';
import { StakingWrapper } from '../utils/staking_wrapper';
@@ -9,8 +7,6 @@ import { DelegatorBalances, StakerBalances } from '../utils/types';
import { StakerActor } from './staker_actor';
const expect = chai.expect;
export class DelegatorActor extends StakerActor {
constructor(owner: string, stakingWrapper: StakingWrapper) {
super(owner, stakingWrapper);
@@ -18,7 +14,7 @@ export class DelegatorActor extends StakerActor {
public async depositZrxAndDelegateToStakingPoolAsync(
poolId: string,
amount: BigNumber,
revertReason?: RevertReason,
revertError?: RevertError,
): Promise<void> {
// query init balances
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
@@ -29,8 +25,8 @@ export class DelegatorActor extends StakerActor {
poolId,
amount,
);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -54,14 +50,14 @@ export class DelegatorActor extends StakerActor {
public async activateAndDelegateStakeAsync(
poolId: string,
amount: BigNumber,
revertReason?: RevertReason,
revertError?: RevertError,
): Promise<void> {
// query init balances
const initDelegatorBalances = await this.getBalancesAsync([poolId]);
// activate and delegate
const txReceiptPromise = this._stakingWrapper.activateAndDelegateStakeAsync(this._owner, poolId, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -89,7 +85,7 @@ export class DelegatorActor extends StakerActor {
public async deactivateAndTimeLockDelegatedStakeAsync(
poolId: string,
amount: BigNumber,
revertReason?: RevertReason,
revertError?: RevertError,
): Promise<void> {
// query init balances
const initDelegatorBalances = await this.getBalancesAsync([poolId]);
@@ -99,8 +95,8 @@ export class DelegatorActor extends StakerActor {
poolId,
amount,
);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;

View File

@@ -1,6 +1,5 @@
import { expectTransactionFailedAsync } from '@0x/contracts-test-utils';
import { RevertReason } from '@0x/types';
import * as chai from 'chai';
import { expect } from '@0x/contracts-test-utils';
import { RevertError } from '@0x/utils';
import * as _ from 'lodash';
import { constants as stakingConstants } from '../utils/constants';
@@ -8,20 +7,18 @@ import { StakingWrapper } from '../utils/staking_wrapper';
import { BaseActor } from './base_actor';
const expect = chai.expect;
export class PoolOperatorActor extends BaseActor {
constructor(owner: string, stakingWrapper: StakingWrapper) {
super(owner, stakingWrapper);
}
public async createStakingPoolAsync(operatorShare: number, revertReason?: RevertReason): Promise<string> {
public async createStakingPoolAsync(operatorShare: number, revertError?: RevertError): Promise<string> {
// query next pool id
const nextPoolId = await this._stakingWrapper.getNextStakingPoolIdAsync();
// create pool
const poolIdPromise = this._stakingWrapper.createStakingPoolAsync(this._owner, operatorShare);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(poolIdPromise, revertReason);
if (revertError !== undefined) {
await expect(poolIdPromise).to.revertWith(revertError);
return '';
}
const poolId = await poolIdPromise;
@@ -33,7 +30,7 @@ export class PoolOperatorActor extends BaseActor {
poolId: string,
makerAddress: string,
makerSignature: string,
revertReason?: RevertReason,
revertError?: RevertError,
): Promise<void> {
// add maker
const txReceiptPromise = this._stakingWrapper.addMakerToStakingPoolAsync(
@@ -42,8 +39,8 @@ export class PoolOperatorActor extends BaseActor {
makerSignature,
this._owner,
);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -57,7 +54,7 @@ export class PoolOperatorActor extends BaseActor {
public async removeMakerFromStakingPoolAsync(
poolId: string,
makerAddress: string,
revertReason?: RevertReason,
revertError?: RevertError,
): Promise<void> {
// remove maker
const txReceiptPromise = this._stakingWrapper.removeMakerFromStakingPoolAsync(
@@ -65,8 +62,8 @@ export class PoolOperatorActor extends BaseActor {
makerAddress,
this._owner,
);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;

View File

@@ -1,7 +1,5 @@
import { expectTransactionFailedAsync } from '@0x/contracts-test-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { expect } from '@0x/contracts-test-utils';
import { BigNumber, RevertError } from '@0x/utils';
import * as _ from 'lodash';
import { StakingWrapper } from '../utils/staking_wrapper';
@@ -9,24 +7,22 @@ import { StakerBalances } from '../utils/types';
import { BaseActor } from './base_actor';
const expect = chai.expect;
export class StakerActor extends BaseActor {
constructor(owner: string, stakingWrapper: StakingWrapper) {
super(owner, stakingWrapper);
}
public async depositZrxAndMintDeactivatedStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async depositZrxAndMintDeactivatedStakeAsync(amount: BigNumber, revertError?: RevertError): Promise<void> {
await this._stakingWrapper.depositZrxAndMintDeactivatedStakeAsync(this._owner, amount);
throw new Error('Checks Unimplemented');
}
public async depositZrxAndMintActivatedStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async depositZrxAndMintActivatedStakeAsync(amount: BigNumber, revertError?: RevertError): Promise<void> {
// query init balances
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
const initStakerBalances = await this.getBalancesAsync();
// deposit stake
const txReceiptPromise = this._stakingWrapper.depositZrxAndMintActivatedStakeAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -42,13 +38,13 @@ export class StakerActor extends BaseActor {
const finalZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
expect(finalZrxBalanceOfVault).to.be.bignumber.equal(initZrxBalanceOfVault.plus(amount));
}
public async activateStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async activateStakeAsync(amount: BigNumber, revertError?: RevertError): Promise<void> {
// query init balances
const initStakerBalances = await this.getBalancesAsync();
// activate stake
const txReceiptPromise = this._stakingWrapper.activateStakeAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -61,13 +57,13 @@ export class StakerActor extends BaseActor {
expectedStakerBalances.deactivatedStakeBalance = initStakerBalances.deactivatedStakeBalance.minus(amount);
await this.assertBalancesAsync(expectedStakerBalances);
}
public async deactivateAndTimeLockStakeAsync(amount: BigNumber, revertReason?: RevertReason): Promise<void> {
public async deactivateAndTimeLockStakeAsync(amount: BigNumber, revertError?: RevertError): Promise<void> {
// query init balances
const initStakerBalances = await this.getBalancesAsync();
// deactivate and timeLock stake
const txReceiptPromise = this._stakingWrapper.deactivateAndTimeLockStakeAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;
@@ -79,17 +75,14 @@ export class StakerActor extends BaseActor {
expectedStakerBalances.deactivatedStakeBalance = initStakerBalances.deactivatedStakeBalance.plus(amount);
await this.assertBalancesAsync(expectedStakerBalances);
}
public async burnDeactivatedStakeAndWithdrawZrxAsync(
amount: BigNumber,
revertReason?: RevertReason,
): Promise<void> {
public async burnDeactivatedStakeAndWithdrawZrxAsync(amount: BigNumber, revertError?: RevertError): Promise<void> {
// query init balances
const initZrxBalanceOfVault = await this._stakingWrapper.getZrxTokenBalanceOfZrxVaultAsync();
const initStakerBalances = await this.getBalancesAsync();
// withdraw stake
const txReceiptPromise = this._stakingWrapper.burnDeactivatedStakeAndWithdrawZrxAsync(this._owner, amount);
if (revertReason !== undefined) {
await expectTransactionFailedAsync(txReceiptPromise, revertReason);
if (revertError !== undefined) {
await expect(txReceiptPromise).to.revertWith(revertError);
return;
}
await txReceiptPromise;

View File

@@ -1,19 +1,14 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { chaiSetup, expectTransactionFailedAsync, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { StakingWrapper } from './utils/staking_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Exchange Integrations', () => {
blockchainTests('Exchange Integrations', env => {
// constants
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
// tokens & addresses
@@ -26,34 +21,22 @@ describe('Exchange Integrations', () => {
let stakingWrapper: StakingWrapper;
let erc20Wrapper: ERC20Wrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// create accounts
accounts = await web3Wrapper.getAvailableAddressesAsync();
accounts = await env.web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
exchange = accounts[1];
// deploy erc20 proxy
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
// deploy zrx token
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// deploy staking contracts
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
stakingWrapper = new StakingWrapper(env.provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
await stakingWrapper.deployAndConfigureContractsAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Exchange Tracking in Staking Contract', () => {
blockchainTests.resets('Exchange Tracking in Staking Contract', () => {
it('basic exchange tracking', async () => {
// 1 try querying an invalid addresses
const invalidAddress = '0x0000000000000000000000000000000000000001';
@@ -64,19 +47,17 @@ describe('Exchange Integrations', () => {
const isValidAddressValid = await stakingWrapper.isValidExchangeAddressAsync(exchange);
expect(isValidAddressValid).to.be.true();
// 3 try adding valid address again
await expectTransactionFailedAsync(
stakingWrapper.addExchangeAddressAsync(exchange),
RevertReason.ExchangeAddressAlreadyRegistered,
);
let revertError = new StakingRevertErrors.ExchangeAddressAlreadyRegisteredError(exchange);
let tx = stakingWrapper.addExchangeAddressAsync(exchange);
await expect(tx).to.revertWith(revertError);
// 4 remove valid address
await stakingWrapper.removeExchangeAddressAsync(exchange);
const isValidAddressStillValid = await stakingWrapper.isValidExchangeAddressAsync(exchange);
expect(isValidAddressStillValid).to.be.false();
// 5 try removing valid address again
await expectTransactionFailedAsync(
stakingWrapper.removeExchangeAddressAsync(exchange),
RevertReason.ExchangeAddressNotRegistered,
);
revertError = new StakingRevertErrors.ExchangeAddressNotRegisteredError(exchange);
tx = stakingWrapper.removeExchangeAddressAsync(exchange);
await expect(tx).to.revertWith(revertError);
// @todo should not be able to add / remove an exchange if not contract owner.
});
});

View File

@@ -1,10 +1,8 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { chaiSetup, expectTransactionFailedAsync, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -13,11 +11,8 @@ import { PoolOperatorActor } from './actors/pool_operator_actor';
import { constants as stakingConstants } from './utils/constants';
import { StakingWrapper } from './utils/staking_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Staking Pool Management', () => {
blockchainTests('Staking Pool Management', env => {
// constants
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
// tokens & addresses
@@ -30,34 +25,22 @@ describe('Staking Pool Management', () => {
let stakingWrapper: StakingWrapper;
let erc20Wrapper: ERC20Wrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// create accounts
accounts = await web3Wrapper.getAvailableAddressesAsync();
accounts = await env.web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
users = accounts.slice(1);
// deploy erc20 proxy
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
// deploy zrx token
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// deploy staking contracts
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
stakingWrapper = new StakingWrapper(env.provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
await stakingWrapper.deployAndConfigureContractsAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Staking Pool Management', () => {
blockchainTests.resets('Staking Pool Management', () => {
it('Should successfully create a pool', async () => {
// test parameters
const operatorAddress = users[0];
@@ -128,13 +111,9 @@ describe('Staking Pool Management', () => {
// add maker to pool
const makerApproval = maker.signApprovalForStakingPool(poolId);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature);
const revertError = new StakingRevertErrors.MakerAddressAlreadyRegisteredError(makerAddress);
// add same maker to pool again
await poolOperator.addMakerToStakingPoolAsync(
poolId,
makerAddress,
makerApproval.signature,
RevertReason.MakerAddressAlreadyRegistered,
);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature, revertError);
});
it('Should fail to remove a maker that does not exist', async () => {
// test parameters
@@ -145,12 +124,13 @@ describe('Staking Pool Management', () => {
// create pool
const poolId = await poolOperator.createStakingPoolAsync(operatorShare);
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
// remove non-existent maker from pool
await poolOperator.removeMakerFromStakingPoolAsync(
poolId,
const revertError = new StakingRevertErrors.MakerAddressNotRegisteredError(
makerAddress,
RevertReason.MakerAddressNotRegistered,
stakingConstants.NIL_POOL_ID,
poolId,
);
// remove non-existent maker from pool
await poolOperator.removeMakerFromStakingPoolAsync(poolId, makerAddress, revertError);
});
it('Should fail to add a maker who signed with the wrong private key', async () => {
// test parameters
@@ -165,14 +145,14 @@ describe('Staking Pool Management', () => {
// create pool
const poolId = await poolOperator.createStakingPoolAsync(operatorShare);
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
// add maker to pool
// add maker to poolxx
const makerApproval = maker.signApprovalForStakingPool(poolId);
await poolOperator.addMakerToStakingPoolAsync(
const revertError = new StakingRevertErrors.InvalidMakerSignatureError(
poolId,
makerAddress,
makerApproval.signature,
RevertReason.InvalidMakerSignature,
);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature, revertError);
});
it('Should fail to add a maker who signed with the wrong staking contract address', async () => {
// test parameters
@@ -188,12 +168,12 @@ describe('Staking Pool Management', () => {
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
// add maker to pool
const makerApproval = maker.signApprovalForStakingPool(poolId);
await poolOperator.addMakerToStakingPoolAsync(
const revertError = new StakingRevertErrors.InvalidMakerSignatureError(
poolId,
makerAddress,
makerApproval.signature,
RevertReason.InvalidMakerSignature,
);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature, revertError);
});
it('Should fail to add a maker who signed with the wrong chain id', async () => {
// test parameters
@@ -216,12 +196,12 @@ describe('Staking Pool Management', () => {
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
// add maker to pool
const makerApproval = maker.signApprovalForStakingPool(poolId);
await poolOperator.addMakerToStakingPoolAsync(
const revertError = new StakingRevertErrors.InvalidMakerSignatureError(
poolId,
makerAddress,
makerApproval.signature,
RevertReason.InvalidMakerSignature,
);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature, revertError);
});
it('Should fail to add a maker when called by someone other than the pool operator', async () => {
// test parameters
@@ -236,15 +216,17 @@ describe('Staking Pool Management', () => {
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
// add maker to pool
const makerApproval = maker.signApprovalForStakingPool(poolId);
await expectTransactionFailedAsync(
stakingWrapper.addMakerToStakingPoolAsync(
poolId,
makerAddress,
makerApproval.signature,
notOperatorAddress,
),
RevertReason.OnlyCallableByPoolOperator,
const revertError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(
notOperatorAddress,
operatorAddress,
);
const tx = stakingWrapper.addMakerToStakingPoolAsync(
poolId,
makerAddress,
makerApproval.signature,
notOperatorAddress,
);
await expect(tx).to.revertWith(revertError);
});
it('Should fail to remove a maker when called by someone other than the pool operator', async () => {
// test parameters
@@ -261,10 +243,13 @@ describe('Staking Pool Management', () => {
const makerApproval = maker.signApprovalForStakingPool(poolId);
await poolOperator.addMakerToStakingPoolAsync(poolId, makerAddress, makerApproval.signature);
// try to remove the maker address from an address other than the operator
await expectTransactionFailedAsync(
stakingWrapper.removeMakerFromStakingPoolAsync(poolId, makerAddress, notOperatorAddress),
RevertReason.OnlyCallableByPoolOperatorOrMaker,
const revertError = new StakingRevertErrors.OnlyCallableByPoolOperatorOrMakerError(
notOperatorAddress,
operatorAddress,
makerAddress,
);
const tx = stakingWrapper.removeMakerFromStakingPoolAsync(poolId, makerAddress, notOperatorAddress);
await expect(tx).to.revertWith(revertError);
});
});
});

View File

@@ -1,17 +1,14 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { chaiSetup, expectTransactionFailedAsync, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { Simulation } from './utils/Simulation';
import { StakingWrapper } from './utils/staking_wrapper';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('End-To-End Simulations', () => {
blockchainTests('End-To-End Simulations', env => {
// constants
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
// tokens & addresses
@@ -25,37 +22,25 @@ describe('End-To-End Simulations', () => {
let stakingWrapper: StakingWrapper;
let erc20Wrapper: ERC20Wrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// create accounts
accounts = await web3Wrapper.getAvailableAddressesAsync();
accounts = await env.web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
exchange = accounts[1];
users = accounts.slice(2);
users = [...users, ...users]; // @TODO figure out how to get more addresses from `web3Wrapper`
// deploy erc20 proxy
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
// deploy zrx token
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// deploy staking contracts
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
stakingWrapper = new StakingWrapper(env.provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
await stakingWrapper.deployAndConfigureContractsAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Simulations', () => {
blockchainTests.resets('Simulations', () => {
it('Should successfully simulate (no delegators / no shadow balances)', async () => {
// @TODO - get computations more accurate
const simulationParams = {
@@ -348,10 +333,9 @@ describe('End-To-End Simulations', () => {
it('Should not be able to record a protocol fee from an unknown exchange', async () => {
const makerAddress = users[1];
const protocolFee = new BigNumber(1);
await expectTransactionFailedAsync(
stakingWrapper.payProtocolFeeAsync(makerAddress, protocolFee, owner),
RevertReason.OnlyCallableByExchange,
);
const revertError = new StakingRevertErrors.OnlyCallableByExchangeError(owner);
const tx = stakingWrapper.payProtocolFeeAsync(makerAddress, protocolFee, owner);
await expect(tx).to.revertWith(revertError);
});
});
});

View File

@@ -1,8 +1,7 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { blockchainTests } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
@@ -10,10 +9,8 @@ import { DelegatorActor } from './actors/delegator_actor';
import { StakerActor } from './actors/staker_actor';
import { StakingWrapper } from './utils/staking_wrapper';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Staking & Delegating', () => {
blockchainTests('Staking & Delegating', env => {
// constants
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
// tokens & addresses
@@ -26,34 +23,22 @@ describe('Staking & Delegating', () => {
let stakingWrapper: StakingWrapper;
let erc20Wrapper: ERC20Wrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// create accounts
accounts = await web3Wrapper.getAvailableAddressesAsync();
accounts = await env.web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
stakers = accounts.slice(2, 5);
// deploy erc20 proxy
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
// deploy zrx token
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// deploy staking contracts
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
stakingWrapper = new StakingWrapper(env.provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
await stakingWrapper.deployAndConfigureContractsAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Staking', () => {
blockchainTests.resets('Staking', () => {
it('basic staking/unstaking', async () => {
// setup test parameters
const amountToStake = StakingWrapper.toBaseUnitAmount(10);
@@ -66,9 +51,10 @@ describe('Staking & Delegating', () => {
await staker.deactivateAndTimeLockStakeAsync(amountToDeactivate);
// note - we cannot re-activate this timeLocked stake until at least one full timeLock period has passed.
// attempting to do so should revert.
await staker.activateStakeAsync(amountToReactivate, RevertReason.InsufficientBalance);
const revertError = new StakingRevertErrors.InsufficientBalanceError(amountToReactivate, 0);
await staker.activateStakeAsync(amountToReactivate, revertError);
await staker.skipToNextTimeLockPeriodAsync();
await staker.activateStakeAsync(amountToReactivate, RevertReason.InsufficientBalance);
await staker.activateStakeAsync(amountToReactivate, revertError);
await staker.skipToNextTimeLockPeriodAsync();
// this forces the internal state to update; it is not necessary to activate stake, but
// allows us to check that state is updated correctly after a timeLock period rolls over.
@@ -79,7 +65,7 @@ describe('Staking & Delegating', () => {
});
});
describe('Delegating', () => {
blockchainTests.resets('Delegating', () => {
it('basic delegating/undelegating', async () => {
// setup test parameters
const amountToDelegate = StakingWrapper.toBaseUnitAmount(10);
@@ -95,9 +81,10 @@ describe('Staking & Delegating', () => {
await delegator.deactivateAndTimeLockDelegatedStakeAsync(poolId, amountToDeactivate);
// note - we cannot re-activate this timeLocked stake until at least one full timeLock period has passed.
// attempting to do so should revert.
await delegator.activateStakeAsync(amountToReactivate, RevertReason.InsufficientBalance);
const revertError = new StakingRevertErrors.InsufficientBalanceError(amountToReactivate, 0);
await delegator.activateStakeAsync(amountToReactivate, revertError);
await delegator.skipToNextTimeLockPeriodAsync();
await delegator.activateStakeAsync(amountToReactivate, RevertReason.InsufficientBalance);
await delegator.activateStakeAsync(amountToReactivate, revertError);
await delegator.skipToNextTimeLockPeriodAsync();
// this forces the internal state to update; it is not necessary to activate stake, but
// allows us to check that state is updated correctly after a timeLock period rolls over.

View File

@@ -660,7 +660,7 @@ export class StakingWrapper {
public async rewardVaultEnterCatastrophicFailureModeAsync(
zeroExMultisigAddress: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingPoolRewardVaultContract().enterCatostrophicFailure.getABIEncodedTransactionData();
const calldata = this.getStakingPoolRewardVaultContract().enterCatastrophicFailure.getABIEncodedTransactionData();
const txReceipt = await this._executeTransactionAsync(calldata, zeroExMultisigAddress);
return txReceipt;
}

View File

@@ -1,17 +1,14 @@
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { chaiSetup, expectTransactionFailedAsync, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { StakingWrapper } from './utils/staking_wrapper';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Staking Vaults', () => {
blockchainTests('Staking Vaults', env => {
// constants
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
// tokens & addresses
@@ -24,34 +21,22 @@ describe('Staking Vaults', () => {
let stakingWrapper: StakingWrapper;
let erc20Wrapper: ERC20Wrapper;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// create accounts
accounts = await web3Wrapper.getAvailableAddressesAsync();
accounts = await env.web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
users = accounts.slice(1);
// deploy erc20 proxy
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
// deploy zrx token
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
await erc20Wrapper.setBalancesAndAllowancesAsync();
// deploy staking contracts
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
stakingWrapper = new StakingWrapper(env.provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
await stakingWrapper.deployAndConfigureContractsAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Reward Vault', () => {
blockchainTests.resets('Reward Vault', () => {
it.skip('basic management', async () => {
// 1 setup test parameters
const poolOperator = users[0];
@@ -62,15 +47,13 @@ describe('Staking Vaults', () => {
// create pool in vault
await stakingWrapper.rewardVaultRegisterPoolAsync(poolId, operatorShare, stakingContractAddress);
// should fail to create pool if it already exists
await expectTransactionFailedAsync(
stakingWrapper.rewardVaultRegisterPoolAsync(poolId, operatorShare, stakingContractAddress),
RevertReason.PoolAlreadyExists,
);
let revertError = new StakingRevertErrors.PoolAlreadyExistsError(poolId);
let tx = stakingWrapper.rewardVaultRegisterPoolAsync(poolId, operatorShare, stakingContractAddress);
await expect(tx).to.revertWith(revertError);
// should fail to create a pool from an address other than the staking contract
await expectTransactionFailedAsync(
stakingWrapper.rewardVaultRegisterPoolAsync(poolId, operatorShare, notStakingContractAddress),
RevertReason.OnlyCallableByStakingContract,
);
revertError = new StakingRevertErrors.OnlyCallableByStakingContractError(notStakingContractAddress);
tx = stakingWrapper.rewardVaultRegisterPoolAsync(poolId, operatorShare, notStakingContractAddress);
await expect(tx).to.revertWith(revertError);
});
});
});

View File

@@ -19,6 +19,7 @@
"generated-artifacts/LibSafeMath64.json",
"generated-artifacts/LibSafeMath96.json",
"generated-artifacts/LibSignatureValidator.json",
"generated-artifacts/LibStakingRichErrors.json",
"generated-artifacts/MixinConstants.json",
"generated-artifacts/MixinDelegatedStake.json",
"generated-artifacts/MixinDeploymentConstants.json",

View File

@@ -164,7 +164,7 @@ function defineBlockchainSuite<T>(
const env = BlockchainTestsEnvironmentSingleton.create();
return describeCall(description, function(this: ISuiteCallbackContext): void {
before(async () => env.blockchainLifecycle.startAsync());
before(async () => env.blockchainLifecycle.revertAsync());
after(async () => env.blockchainLifecycle.revertAsync());
callback.call(this, env);
});
}

View File

@@ -16,8 +16,8 @@ library LibSafeMath {
}
uint256 c = a * b;
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_MULTIPLICATION_OVERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
@@ -31,8 +31,8 @@ library LibSafeMath {
returns (uint256)
{
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_DIVISION_BY_ZERO,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
@@ -47,8 +47,8 @@ library LibSafeMath {
returns (uint256)
{
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_SUBTRACTION_UNDERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
@@ -63,8 +63,8 @@ library LibSafeMath {
{
uint256 c = a + b;
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_ADDITION_OVERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));

View File

@@ -3,20 +3,37 @@ pragma solidity ^0.5.9;
library LibSafeMathRichErrors {
// bytes4(keccak256("SafeMathError(uint8,uint256,uint256)"))
bytes4 internal constant SAFE_MATH_ERROR =
0x35a51a70;
// bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)"))
bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR =
0xe946c1bb;
enum SafeMathErrorCodes {
UINT256_ADDITION_OVERFLOW,
UINT256_MULTIPLICATION_OVERFLOW,
UINT256_SUBTRACTION_UNDERFLOW,
UINT256_DIVISION_BY_ZERO
// bytes4(keccak256("Uint96BinOpError(uint8,uint96,uint96)"))
bytes4 internal constant UINT96_BINOP_ERROR_SELECTOR =
0xe486a353;
// bytes4(keccak256("Uint64BinOpError(uint8,uint64,uint64)"))
bytes4 internal constant UINT64_BINOP_ERROR_SELECTOR =
0x67e71b32;
// bytes4(keccak256("Uint256DowncastError(uint8,uint256)"))
bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR =
0xc996af7b;
enum BinOpErrorCodes {
ADDITION_OVERFLOW,
MULTIPLICATION_OVERFLOW,
SUBTRACTION_UNDERFLOW,
DIVISION_BY_ZERO
}
enum DowncastErrorCodes {
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96
}
// solhint-disable func-name-mixedcase
function SafeMathError(
SafeMathErrorCodes errorCode,
function Uint256BinOpError(
BinOpErrorCodes errorCode,
uint256 a,
uint256 b
)
@@ -25,10 +42,59 @@ library LibSafeMathRichErrors {
returns (bytes memory)
{
return abi.encodeWithSelector(
SAFE_MATH_ERROR,
UINT256_BINOP_ERROR_SELECTOR,
errorCode,
a,
b
);
}
function Uint96BinOpError(
BinOpErrorCodes errorCode,
uint96 a,
uint96 b
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UINT96_BINOP_ERROR_SELECTOR,
errorCode,
a,
b
);
}
function Uint64BinOpError(
BinOpErrorCodes errorCode,
uint64 a,
uint64 b
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UINT64_BINOP_ERROR_SELECTOR,
errorCode,
a,
b
);
}
function Uint256DowncastError(
DowncastErrorCodes errorCode,
uint256 a
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
UINT256_DOWNCAST_ERROR_SELECTOR,
errorCode,
a
);
}
}

View File

@@ -16,8 +16,8 @@ contract SafeMath {
}
uint256 c = a * b;
if (c / a != b) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_MULTIPLICATION_OVERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
a,
b
));
@@ -31,8 +31,8 @@ contract SafeMath {
returns (uint256)
{
if (b == 0) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_DIVISION_BY_ZERO,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
a,
b
));
@@ -47,8 +47,8 @@ contract SafeMath {
returns (uint256)
{
if (b > a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_SUBTRACTION_UNDERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
a,
b
));
@@ -63,8 +63,8 @@ contract SafeMath {
{
uint256 c = a + b;
if (c < a) {
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_ADDITION_OVERFLOW,
LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
a,
b
));

View File

@@ -8,11 +8,7 @@ const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
export function safeAdd(a: BigNumber, b: BigNumber): BigNumber {
const r = a.plus(b);
if (r.isGreaterThan(MAX_UINT256)) {
throw new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
a,
b,
);
throw new SafeMathRevertErrors.Uint256BinOpError(SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow, a, b);
}
return r;
}
@@ -23,8 +19,8 @@ export function safeAdd(a: BigNumber, b: BigNumber): BigNumber {
export function safeSub(a: BigNumber, b: BigNumber): BigNumber {
const r = a.minus(b);
if (r.isLessThan(0)) {
throw new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
throw new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
a,
b,
);
@@ -38,8 +34,8 @@ export function safeSub(a: BigNumber, b: BigNumber): BigNumber {
export function safeMul(a: BigNumber, b: BigNumber): BigNumber {
const r = a.times(b);
if (r.isGreaterThan(MAX_UINT256)) {
throw new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
throw new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
@@ -52,11 +48,7 @@ export function safeMul(a: BigNumber, b: BigNumber): BigNumber {
*/
export function safeDiv(a: BigNumber, b: BigNumber): BigNumber {
if (b.isEqualTo(0)) {
throw new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
a,
b,
);
throw new SafeMathRevertErrors.Uint256BinOpError(SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero, a, b);
}
return a.dividedToIntegerBy(b);
}

View File

@@ -21,8 +21,8 @@ describe('Reference Functions', () => {
it('reverts on overflow', () => {
const a = MAX_UINT256.dividedToIntegerBy(2);
const b = MAX_UINT256.dividedToIntegerBy(2).plus(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);
@@ -41,8 +41,8 @@ describe('Reference Functions', () => {
it('reverts on underflow', () => {
const a = MAX_UINT256.dividedToIntegerBy(2);
const b = MAX_UINT256.dividedToIntegerBy(2).plus(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
a,
b,
);
@@ -61,8 +61,8 @@ describe('Reference Functions', () => {
it('reverts on overflow', () => {
const a = MAX_UINT256.dividedToIntegerBy(2);
const b = MAX_UINT256.dividedToIntegerBy(2).plus(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
@@ -81,8 +81,8 @@ describe('Reference Functions', () => {
it('reverts if denominator is zero', () => {
const a = MAX_UINT256.dividedToIntegerBy(2);
const b = ZERO_AMOUNT;
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
a,
b,
);

View File

@@ -45,8 +45,8 @@ blockchainTests('SafeMath', env => {
it('should revert if the multiplication overflows', async () => {
const a = toBigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBigNumber(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
@@ -91,8 +91,8 @@ blockchainTests('SafeMath', env => {
it('should revert if second argument is zero', async () => {
const a = toBigNumber(1);
const b = toBigNumber(0);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256DivisionByZero,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.DivisionByZero,
a,
b,
);
@@ -112,8 +112,8 @@ blockchainTests('SafeMath', env => {
it('should revert if the subtraction underflows', async () => {
const a = toBigNumber(0);
const b = toBigNumber(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
a,
b,
);
@@ -143,8 +143,8 @@ blockchainTests('SafeMath', env => {
it('should revert if the addition overflows', async () => {
const a = toBigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBigNumber(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);