Merge pull request #2217 from 0xProject/feature/contracts-staking/mixin-stake-storage-unit-tests
MixinStakeStorage unit tests
This commit is contained in:
commit
f5ad65bb8a
98
contracts/staking/contracts/test/TestMixinStakeStorage.sol
Normal file
98
contracts/staking/contracts/test/TestMixinStakeStorage.sol
Normal 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;
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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';
|
||||
|
213
contracts/staking/test/unit_tests/mixin_stake_storage_test.ts
Normal file
213
contracts/staking/test/unit_tests/mixin_stake_storage_test.ts
Normal 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),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -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;
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user