Merge pull request #2292 from 0xProject/feat/staking/MixinStakeBalances-unit-tests
Add MixinStakeBalances unit tests.
This commit is contained in:
122
contracts/staking/contracts/test/TestMixinStakeBalances.sol
Normal file
122
contracts/staking/contracts/test/TestMixinStakeBalances.sol
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "./TestStakingNoWETH.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestMixinStakeBalances is
|
||||||
|
TestStakingNoWETH
|
||||||
|
{
|
||||||
|
uint256 private _balanceOfZrxVault;
|
||||||
|
mapping (address => uint256) private _zrxBalanceOf;
|
||||||
|
|
||||||
|
function setBalanceOfZrxVault(uint256 balance)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_balanceOfZrxVault = balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setZrxBalanceOf(address staker, uint256 balance)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
_zrxBalanceOf[staker] = balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `IZrxVault.balanceOfZrxVault`
|
||||||
|
function balanceOfZrxVault()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _balanceOfZrxVault;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `IZrxVault.balanceOf`
|
||||||
|
function balanceOf(address staker)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _zrxBalanceOf[staker];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set `_ownerStakeByStatus`
|
||||||
|
function setOwnerStakeByStatus(
|
||||||
|
address owner,
|
||||||
|
IStructs.StakeStatus status,
|
||||||
|
IStructs.StoredBalance memory stake
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_ownerStakeByStatus[uint8(status)][owner] = stake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set `_delegatedStakeToPoolByOwner`
|
||||||
|
function setDelegatedStakeToPoolByOwner(
|
||||||
|
address owner,
|
||||||
|
bytes32 poolId,
|
||||||
|
IStructs.StoredBalance memory stake
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_delegatedStakeToPoolByOwner[owner][poolId] = stake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set `_delegatedStakeByPoolId`
|
||||||
|
function setDelegatedStakeByPoolId(
|
||||||
|
bytes32 poolId,
|
||||||
|
IStructs.StoredBalance memory stake
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_delegatedStakeByPoolId[poolId] = stake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set `_globalStakeByStatus`
|
||||||
|
function setGlobalStakeByStatus(
|
||||||
|
IStructs.StakeStatus status,
|
||||||
|
IStructs.StoredBalance memory stake
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_globalStakeByStatus[uint8(status)] = stake;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridden to use this contract as the ZRX vault.
|
||||||
|
function getZrxVault()
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (IZrxVault zrxVault)
|
||||||
|
{
|
||||||
|
return IZrxVault(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridden to just return the input with the epoch incremented.
|
||||||
|
function _loadCurrentBalance(IStructs.StoredBalance storage balancePtr)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IStructs.StoredBalance memory balance)
|
||||||
|
{
|
||||||
|
balance = balancePtr;
|
||||||
|
balance.currentEpoch += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json'
|
|||||||
import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json';
|
import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json';
|
||||||
import * as TestMixinParams from '../generated-artifacts/TestMixinParams.json';
|
import * as TestMixinParams from '../generated-artifacts/TestMixinParams.json';
|
||||||
import * as TestMixinStake from '../generated-artifacts/TestMixinStake.json';
|
import * as TestMixinStake from '../generated-artifacts/TestMixinStake.json';
|
||||||
|
import * as TestMixinStakeBalances from '../generated-artifacts/TestMixinStakeBalances.json';
|
||||||
import * as TestMixinStakeStorage from '../generated-artifacts/TestMixinStakeStorage.json';
|
import * as TestMixinStakeStorage from '../generated-artifacts/TestMixinStakeStorage.json';
|
||||||
import * as TestMixinStakingPool from '../generated-artifacts/TestMixinStakingPool.json';
|
import * as TestMixinStakingPool from '../generated-artifacts/TestMixinStakingPool.json';
|
||||||
import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json';
|
import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json';
|
||||||
@@ -95,6 +96,7 @@ export const artifacts = {
|
|||||||
TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact,
|
TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact,
|
||||||
TestMixinParams: TestMixinParams as ContractArtifact,
|
TestMixinParams: TestMixinParams as ContractArtifact,
|
||||||
TestMixinStake: TestMixinStake as ContractArtifact,
|
TestMixinStake: TestMixinStake as ContractArtifact,
|
||||||
|
TestMixinStakeBalances: TestMixinStakeBalances as ContractArtifact,
|
||||||
TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact,
|
TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact,
|
||||||
TestMixinStakingPool: TestMixinStakingPool as ContractArtifact,
|
TestMixinStakingPool: TestMixinStakingPool as ContractArtifact,
|
||||||
TestProtocolFees: TestProtocolFees as ContractArtifact,
|
TestProtocolFees: TestProtocolFees as ContractArtifact,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export * from '../generated-wrappers/test_lib_fixed_math';
|
|||||||
export * from '../generated-wrappers/test_lib_safe_downcast';
|
export * from '../generated-wrappers/test_lib_safe_downcast';
|
||||||
export * from '../generated-wrappers/test_mixin_params';
|
export * from '../generated-wrappers/test_mixin_params';
|
||||||
export * from '../generated-wrappers/test_mixin_stake';
|
export * from '../generated-wrappers/test_mixin_stake';
|
||||||
|
export * from '../generated-wrappers/test_mixin_stake_balances';
|
||||||
export * from '../generated-wrappers/test_mixin_stake_storage';
|
export * from '../generated-wrappers/test_mixin_stake_storage';
|
||||||
export * from '../generated-wrappers/test_mixin_staking_pool';
|
export * from '../generated-wrappers/test_mixin_staking_pool';
|
||||||
export * from '../generated-wrappers/test_protocol_fees';
|
export * from '../generated-wrappers/test_protocol_fees';
|
||||||
|
|||||||
223
contracts/staking/test/unit_tests/stake_balances_test.ts
Normal file
223
contracts/staking/test/unit_tests/stake_balances_test.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
hexRandom,
|
||||||
|
randomAddress,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
|
import { artifacts, TestMixinStakeBalancesContract } from '../../src';
|
||||||
|
import { constants as stakingConstants } from '../utils/constants';
|
||||||
|
import { StakeStatus, StoredBalance } from '../utils/types';
|
||||||
|
|
||||||
|
blockchainTests.resets('MixinStakeBalances unit tests', env => {
|
||||||
|
let testContract: TestMixinStakeBalancesContract;
|
||||||
|
const { INITIAL_EPOCH } = stakingConstants;
|
||||||
|
const CURRENT_EPOCH = INITIAL_EPOCH.plus(1);
|
||||||
|
const EMPTY_BALANCE = {
|
||||||
|
currentEpochBalance: constants.ZERO_AMOUNT,
|
||||||
|
nextEpochBalance: constants.ZERO_AMOUNT,
|
||||||
|
currentEpoch: new BigNumber(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestMixinStakeBalancesContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestMixinStakeBalances,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function randomAmount(): BigNumber {
|
||||||
|
return getRandomInteger(1, 100e18);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomStoredBalance(): StoredBalance {
|
||||||
|
return {
|
||||||
|
currentEpochBalance: randomAmount(),
|
||||||
|
nextEpochBalance: randomAmount(),
|
||||||
|
currentEpoch: INITIAL_EPOCH,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirrors the behavior of the `_loadCurrentBalance()` override in
|
||||||
|
// `TestMixinStakeBalances`.
|
||||||
|
function toCurrentBalance(balance: StoredBalance): StoredBalance {
|
||||||
|
return {
|
||||||
|
...balance,
|
||||||
|
currentEpoch: balance.currentEpoch.plus(1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getGlobalStakeByStatus()', () => {
|
||||||
|
const delegatedBalance = randomStoredBalance();
|
||||||
|
const zrxVaultBalance = randomAmount().plus(
|
||||||
|
BigNumber.max(delegatedBalance.currentEpochBalance, delegatedBalance.nextEpochBalance),
|
||||||
|
);
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.setGlobalStakeByStatus.awaitTransactionSuccessAsync(
|
||||||
|
StakeStatus.Delegated,
|
||||||
|
delegatedBalance,
|
||||||
|
);
|
||||||
|
await testContract.setBalanceOfZrxVault.awaitTransactionSuccessAsync(zrxVaultBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('undelegated stake is the difference between zrx vault balance and global delegated stake', async () => {
|
||||||
|
const expectedBalance = {
|
||||||
|
currentEpoch: CURRENT_EPOCH,
|
||||||
|
currentEpochBalance: zrxVaultBalance.minus(delegatedBalance.currentEpochBalance),
|
||||||
|
nextEpochBalance: zrxVaultBalance.minus(delegatedBalance.nextEpochBalance),
|
||||||
|
};
|
||||||
|
const actualBalance = await testContract.getGlobalStakeByStatus.callAsync(StakeStatus.Undelegated);
|
||||||
|
expect(actualBalance).to.deep.eq(expectedBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delegated stake is the global delegated stake', async () => {
|
||||||
|
const actualBalance = await testContract.getGlobalStakeByStatus.callAsync(StakeStatus.Delegated);
|
||||||
|
expect(actualBalance).to.deep.eq(toCurrentBalance(delegatedBalance));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('undelegated stake throws if the zrx vault balance is below the delegated stake balance', async () => {
|
||||||
|
const _zrxVaultBalance = BigNumber.min(
|
||||||
|
delegatedBalance.currentEpochBalance,
|
||||||
|
delegatedBalance.nextEpochBalance,
|
||||||
|
).minus(1);
|
||||||
|
await testContract.setBalanceOfZrxVault.awaitTransactionSuccessAsync(_zrxVaultBalance);
|
||||||
|
const tx = testContract.getGlobalStakeByStatus.callAsync(StakeStatus.Undelegated);
|
||||||
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
||||||
|
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
|
||||||
|
_zrxVaultBalance,
|
||||||
|
delegatedBalance.currentEpochBalance.gt(_zrxVaultBalance)
|
||||||
|
? delegatedBalance.currentEpochBalance
|
||||||
|
: delegatedBalance.nextEpochBalance,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if unknown stake status is passed in', async () => {
|
||||||
|
const tx = testContract.getGlobalStakeByStatus.callAsync(2);
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOwnerStakeByStatus()', () => {
|
||||||
|
const staker = randomAddress();
|
||||||
|
const notStaker = randomAddress();
|
||||||
|
const delegatedStake = randomStoredBalance();
|
||||||
|
const undelegatedStake = randomStoredBalance();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.setOwnerStakeByStatus.awaitTransactionSuccessAsync(
|
||||||
|
staker,
|
||||||
|
StakeStatus.Delegated,
|
||||||
|
delegatedStake,
|
||||||
|
);
|
||||||
|
await testContract.setOwnerStakeByStatus.awaitTransactionSuccessAsync(
|
||||||
|
staker,
|
||||||
|
StakeStatus.Undelegated,
|
||||||
|
undelegatedStake,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if unknown stake status is passed in', async () => {
|
||||||
|
const tx = testContract.getOwnerStakeByStatus.callAsync(staker, 2);
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty delegated stake for an unstaked owner', async () => {
|
||||||
|
const balance = await testContract.getOwnerStakeByStatus.callAsync(notStaker, StakeStatus.Delegated);
|
||||||
|
expect(balance).to.deep.eq(EMPTY_BALANCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty undelegated stake for an unstaked owner', async () => {
|
||||||
|
const balance = await testContract.getOwnerStakeByStatus.callAsync(notStaker, StakeStatus.Undelegated);
|
||||||
|
expect(balance).to.deep.eq(EMPTY_BALANCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undelegated stake for a staked owner', async () => {
|
||||||
|
const balance = await testContract.getOwnerStakeByStatus.callAsync(staker, StakeStatus.Undelegated);
|
||||||
|
expect(balance).to.deep.eq(toCurrentBalance(undelegatedStake));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns delegated stake for a staked owner', async () => {
|
||||||
|
const balance = await testContract.getOwnerStakeByStatus.callAsync(staker, StakeStatus.Delegated);
|
||||||
|
expect(balance).to.deep.eq(toCurrentBalance(delegatedStake));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTotalStake()', () => {
|
||||||
|
const staker = randomAddress();
|
||||||
|
const notStaker = randomAddress();
|
||||||
|
const stakerAmount = randomAmount();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.setZrxBalanceOf.awaitTransactionSuccessAsync(staker, stakerAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for unstaked owner', async () => {
|
||||||
|
const amount = await testContract.getTotalStake.callAsync(notStaker);
|
||||||
|
expect(amount).to.bignumber.eq(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns stake for staked owner', async () => {
|
||||||
|
const amount = await testContract.getTotalStake.callAsync(staker);
|
||||||
|
expect(amount).to.bignumber.eq(stakerAmount);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getStakeDelegatedToPoolByOwner()', () => {
|
||||||
|
const staker = randomAddress();
|
||||||
|
const notStaker = randomAddress();
|
||||||
|
const poolId = hexRandom();
|
||||||
|
const notPoolId = hexRandom();
|
||||||
|
const delegatedBalance = randomStoredBalance();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.setDelegatedStakeToPoolByOwner.awaitTransactionSuccessAsync(
|
||||||
|
staker,
|
||||||
|
poolId,
|
||||||
|
delegatedBalance,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for unstaked owner', async () => {
|
||||||
|
const balance = await testContract.getStakeDelegatedToPoolByOwner.callAsync(notStaker, poolId);
|
||||||
|
expect(balance).to.deep.eq(EMPTY_BALANCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for empty pool', async () => {
|
||||||
|
const balance = await testContract.getStakeDelegatedToPoolByOwner.callAsync(staker, notPoolId);
|
||||||
|
expect(balance).to.deep.eq(EMPTY_BALANCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns stake for staked owner in their pool', async () => {
|
||||||
|
const balance = await testContract.getStakeDelegatedToPoolByOwner.callAsync(staker, poolId);
|
||||||
|
expect(balance).to.deep.eq(toCurrentBalance(delegatedBalance));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTotalStakeDelegatedToPool()', () => {
|
||||||
|
const poolId = hexRandom();
|
||||||
|
const notPoolId = hexRandom();
|
||||||
|
const delegatedBalance = randomStoredBalance();
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await testContract.setDelegatedStakeByPoolId.awaitTransactionSuccessAsync(poolId, delegatedBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for empty pool', async () => {
|
||||||
|
const balance = await testContract.getTotalStakeDelegatedToPool.callAsync(notPoolId);
|
||||||
|
expect(balance).to.deep.eq(EMPTY_BALANCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns stake for staked pool', async () => {
|
||||||
|
const balance = await testContract.getTotalStakeDelegatedToPool.callAsync(poolId);
|
||||||
|
expect(balance).to.deep.eq(toCurrentBalance(delegatedBalance));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable: max-file-line-count
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"generated-artifacts/TestLibSafeDowncast.json",
|
"generated-artifacts/TestLibSafeDowncast.json",
|
||||||
"generated-artifacts/TestMixinParams.json",
|
"generated-artifacts/TestMixinParams.json",
|
||||||
"generated-artifacts/TestMixinStake.json",
|
"generated-artifacts/TestMixinStake.json",
|
||||||
|
"generated-artifacts/TestMixinStakeBalances.json",
|
||||||
"generated-artifacts/TestMixinStakeStorage.json",
|
"generated-artifacts/TestMixinStakeStorage.json",
|
||||||
"generated-artifacts/TestMixinStakingPool.json",
|
"generated-artifacts/TestMixinStakingPool.json",
|
||||||
"generated-artifacts/TestProtocolFees.json",
|
"generated-artifacts/TestProtocolFees.json",
|
||||||
|
|||||||
Reference in New Issue
Block a user