Merge pull request #2217 from 0xProject/feature/contracts-staking/mixin-stake-storage-unit-tests

MixinStakeStorage unit tests
This commit is contained in:
mzhu25 2019-09-27 13:20:42 -07:00 committed by GitHub
commit f5ad65bb8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 323 additions and 1 deletions

View File

@ -0,0 +1,98 @@
/*
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;
pragma experimental ABIEncoderV2;
import "../src/interfaces/IStructs.sol";
import "../src/stake/MixinStakeStorage.sol";
contract TestMixinStakeStorage is
MixinStakeStorage
{
mapping (uint256 => IStructs.StoredBalance) public testBalances;
function setCurrentEpoch(uint256 newEpoch)
external
{
currentEpoch = newEpoch;
}
function moveStake(
uint256 fromIndex,
uint256 toIndex,
uint256 amount
)
external
{
return _moveStake(
testBalances[fromIndex],
testBalances[toIndex],
amount
);
}
function increaseCurrentAndNextBalance(uint256 index, uint256 amount)
external
{
return _increaseCurrentAndNextBalance(testBalances[index], amount);
}
function decreaseCurrentAndNextBalance(uint256 index, uint256 amount)
external
{
_decreaseCurrentAndNextBalance(testBalances[index], amount);
}
function increaseNextBalance(uint256 index, uint256 amount)
external
{
_increaseNextBalance(testBalances[index], amount);
}
function decreaseNextBalance(uint256 index, uint256 amount)
external
{
_decreaseNextBalance(testBalances[index], amount);
}
function loadSyncedBalance(uint256 index)
external
returns (IStructs.StoredBalance memory balance)
{
return _loadSyncedBalance(testBalances[index]);
}
function loadUnsyncedBalance(uint256 index)
external
view
returns (IStructs.StoredBalance memory balance)
{
return _loadUnsyncedBalance(testBalances[index]);
}
function setStoredBalance(
IStructs.StoredBalance memory balance,
uint256 index
)
public
{
testBalances[index] = balance;
}
}

View File

@ -37,7 +37,7 @@
},
"config": {
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault).json"
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinStakeStorage|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault).json"
},
"repository": {
"type": "git",

View File

@ -47,6 +47,7 @@ import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json'
import * as TestLibProxy from '../generated-artifacts/TestLibProxy.json';
import * as TestLibProxyReceiver from '../generated-artifacts/TestLibProxyReceiver.json';
import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json';
import * as TestMixinStakeStorage from '../generated-artifacts/TestMixinStakeStorage.json';
import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json';
import * as TestStaking from '../generated-artifacts/TestStaking.json';
import * as TestStakingNoWETH from '../generated-artifacts/TestStakingNoWETH.json';
@ -97,6 +98,7 @@ export const artifacts = {
TestLibProxy: TestLibProxy as ContractArtifact,
TestLibProxyReceiver: TestLibProxyReceiver as ContractArtifact,
TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact,
TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact,
TestProtocolFees: TestProtocolFees as ContractArtifact,
TestStaking: TestStaking as ContractArtifact,
TestStakingNoWETH: TestStakingNoWETH as ContractArtifact,

View File

@ -45,6 +45,7 @@ export * from '../generated-wrappers/test_lib_fixed_math';
export * from '../generated-wrappers/test_lib_proxy';
export * from '../generated-wrappers/test_lib_proxy_receiver';
export * from '../generated-wrappers/test_lib_safe_downcast';
export * from '../generated-wrappers/test_mixin_stake_storage';
export * from '../generated-wrappers/test_protocol_fees';
export * from '../generated-wrappers/test_staking';
export * from '../generated-wrappers/test_staking_no_w_e_t_h';

View File

@ -0,0 +1,213 @@
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { StoredBalance } from '../utils/types';
import { artifacts, TestMixinStakeStorageContract } from '../../src';
import { constants } from '../utils/constants';
blockchainTests.resets('MixinStakeStorage unit tests', env => {
let testContract: TestMixinStakeStorageContract;
let defaultUninitializedBalance: StoredBalance;
let defaultSyncedBalance: StoredBalance;
let defaultUnsyncedBalance: StoredBalance;
const CURRENT_EPOCH = new BigNumber(5);
const INDEX_ZERO = new BigNumber(0);
const INDEX_ONE = new BigNumber(1);
before(async () => {
testContract = await TestMixinStakeStorageContract.deployFrom0xArtifactAsync(
artifacts.TestMixinStakeStorage,
env.provider,
env.txDefaults,
artifacts,
);
await testContract.setCurrentEpoch.awaitTransactionSuccessAsync(CURRENT_EPOCH);
defaultUninitializedBalance = {
isInitialized: false,
currentEpoch: constants.INITIAL_EPOCH,
currentEpochBalance: new BigNumber(0),
nextEpochBalance: new BigNumber(0),
};
defaultSyncedBalance = {
isInitialized: true,
currentEpoch: CURRENT_EPOCH,
currentEpochBalance: new BigNumber(16),
nextEpochBalance: new BigNumber(16),
};
defaultUnsyncedBalance = {
isInitialized: true,
currentEpoch: CURRENT_EPOCH.minus(1),
currentEpochBalance: new BigNumber(10),
nextEpochBalance: new BigNumber(16),
};
});
async function getTestBalancesAsync(index: Numberish): Promise<StoredBalance> {
const storedBalance: Partial<StoredBalance> = {};
[
storedBalance.isInitialized,
storedBalance.currentEpoch,
storedBalance.currentEpochBalance,
storedBalance.nextEpochBalance,
] = await testContract.testBalances.callAsync(new BigNumber(index));
return storedBalance as StoredBalance;
}
describe('Move stake', () => {
async function moveStakeAndVerifyBalancesAsync(
fromBalance: StoredBalance,
toBalance: StoredBalance,
amount: BigNumber,
): Promise<void> {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(fromBalance, INDEX_ZERO);
await testContract.setStoredBalance.awaitTransactionSuccessAsync(toBalance, INDEX_ONE);
await testContract.moveStake.awaitTransactionSuccessAsync(INDEX_ZERO, INDEX_ONE, amount);
const actualBalances = await Promise.all([
getTestBalancesAsync(INDEX_ZERO),
getTestBalancesAsync(INDEX_ONE),
]);
expect(actualBalances[0]).to.deep.equal({
isInitialized: true,
currentEpoch: CURRENT_EPOCH,
currentEpochBalance: fromBalance.currentEpochBalance,
nextEpochBalance: fromBalance.nextEpochBalance.minus(amount),
});
expect(actualBalances[1]).to.deep.equal({
isInitialized: true,
currentEpoch: CURRENT_EPOCH,
currentEpochBalance: toBalance.currentEpochBalance,
nextEpochBalance: toBalance.nextEpochBalance.plus(amount),
});
}
it('Updates balances to reflect move', async () => {
await moveStakeAndVerifyBalancesAsync(
defaultSyncedBalance,
defaultSyncedBalance,
defaultSyncedBalance.nextEpochBalance.dividedToIntegerBy(2),
);
});
it('Can move amount equal to next epoch balance', async () => {
await moveStakeAndVerifyBalancesAsync(
defaultSyncedBalance,
defaultSyncedBalance,
defaultSyncedBalance.nextEpochBalance,
);
});
it('Moves to and initializes a previously uninitalized balance', async () => {
await moveStakeAndVerifyBalancesAsync(
defaultSyncedBalance,
defaultUninitializedBalance,
defaultSyncedBalance.nextEpochBalance.dividedToIntegerBy(2),
);
});
it('Noop if pointers are equal', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultSyncedBalance, INDEX_ZERO);
// If the pointers weren't equal, this would revert with InsufficientBalanceError
await testContract.moveStake.awaitTransactionSuccessAsync(
INDEX_ZERO,
INDEX_ZERO,
defaultSyncedBalance.nextEpochBalance.plus(1),
);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultSyncedBalance);
});
it("Reverts if attempting to move more than next epoch's balance", async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultSyncedBalance, INDEX_ZERO);
const amount = defaultSyncedBalance.nextEpochBalance.plus(1);
const tx = testContract.moveStake.awaitTransactionSuccessAsync(INDEX_ZERO, INDEX_ONE, amount);
await expect(tx).to.revertWith(
new StakingRevertErrors.InsufficientBalanceError(amount, defaultSyncedBalance.nextEpochBalance),
);
});
});
describe('Load balance', () => {
it('_loadSyncedBalance does not change state if balance was previously synced in the current epoch', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultSyncedBalance, INDEX_ZERO);
const actualBalance = await testContract.loadSyncedBalance.callAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultSyncedBalance);
});
it('_loadSyncedBalance updates current epoch fields if the balance has not yet been synced in the current epoch', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const actualBalance = await testContract.loadSyncedBalance.callAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultSyncedBalance);
});
it('_loadUnsyncedBalance loads unsynced balance from storage without changing fields', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const actualBalance = await testContract.loadUnsyncedBalance.callAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultUnsyncedBalance);
});
it('_loadUnsyncedBalance loads synced balance from storage without changing fields', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultSyncedBalance, INDEX_ZERO);
const actualBalance = await testContract.loadUnsyncedBalance.callAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultSyncedBalance);
});
});
describe('Increase/decrease balance', () => {
it('_increaseCurrentAndNextBalance', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const amount = defaultUnsyncedBalance.currentEpochBalance.dividedToIntegerBy(2);
await testContract.increaseCurrentAndNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal({
...defaultSyncedBalance,
currentEpochBalance: defaultSyncedBalance.currentEpochBalance.plus(amount),
nextEpochBalance: defaultSyncedBalance.nextEpochBalance.plus(amount),
});
});
it('_increaseCurrentAndNextBalance (previously uninitialized)', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUninitializedBalance, INDEX_ZERO);
const amount = defaultSyncedBalance.currentEpochBalance;
await testContract.increaseCurrentAndNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal(defaultSyncedBalance);
});
it('_decreaseCurrentAndNextBalance', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const amount = defaultUnsyncedBalance.currentEpochBalance.dividedToIntegerBy(2);
await testContract.decreaseCurrentAndNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal({
...defaultSyncedBalance,
currentEpochBalance: defaultSyncedBalance.currentEpochBalance.minus(amount),
nextEpochBalance: defaultSyncedBalance.nextEpochBalance.minus(amount),
});
});
it('_increaseNextBalance', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const amount = defaultUnsyncedBalance.currentEpochBalance.dividedToIntegerBy(2);
await testContract.increaseNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal({
...defaultSyncedBalance,
nextEpochBalance: defaultSyncedBalance.nextEpochBalance.plus(amount),
});
});
it('_increaseCurrentAndNextBalance (previously uninitialized)', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUninitializedBalance, INDEX_ZERO);
const amount = defaultSyncedBalance.currentEpochBalance;
await testContract.increaseNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal({
...defaultSyncedBalance,
currentEpochBalance: new BigNumber(0),
});
});
it('_decreaseNextBalance', async () => {
await testContract.setStoredBalance.awaitTransactionSuccessAsync(defaultUnsyncedBalance, INDEX_ZERO);
const amount = defaultUnsyncedBalance.currentEpochBalance.dividedToIntegerBy(2);
await testContract.decreaseNextBalance.awaitTransactionSuccessAsync(INDEX_ZERO, amount);
const actualBalance = await getTestBalancesAsync(INDEX_ZERO);
expect(actualBalance).to.deep.equal({
...defaultSyncedBalance,
nextEpochBalance: defaultSyncedBalance.nextEpochBalance.minus(amount),
});
});
});
});

View File

@ -59,6 +59,13 @@ export interface EndOfEpochInfo {
totalWeightedStake: BigNumber;
}
export interface StoredBalance {
isInitialized: boolean;
currentEpoch: number | BigNumber;
currentEpochBalance: BigNumber;
nextEpochBalance: BigNumber;
}
export interface StakeBalance {
currentEpochBalance: BigNumber;
nextEpochBalance: BigNumber;

View File

@ -45,6 +45,7 @@
"generated-artifacts/TestLibProxy.json",
"generated-artifacts/TestLibProxyReceiver.json",
"generated-artifacts/TestLibSafeDowncast.json",
"generated-artifacts/TestMixinStakeStorage.json",
"generated-artifacts/TestProtocolFees.json",
"generated-artifacts/TestStaking.json",
"generated-artifacts/TestStakingNoWETH.json",