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 TestMixinParams from '../generated-artifacts/TestMixinParams.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 TestMixinStakingPool from '../generated-artifacts/TestMixinStakingPool.json'; | ||||
| import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json'; | ||||
| @@ -95,6 +96,7 @@ export const artifacts = { | ||||
|     TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact, | ||||
|     TestMixinParams: TestMixinParams as ContractArtifact, | ||||
|     TestMixinStake: TestMixinStake as ContractArtifact, | ||||
|     TestMixinStakeBalances: TestMixinStakeBalances as ContractArtifact, | ||||
|     TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact, | ||||
|     TestMixinStakingPool: TestMixinStakingPool 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_mixin_params'; | ||||
| 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_staking_pool'; | ||||
| 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/TestMixinParams.json", | ||||
|         "generated-artifacts/TestMixinStake.json", | ||||
|         "generated-artifacts/TestMixinStakeBalances.json", | ||||
|         "generated-artifacts/TestMixinStakeStorage.json", | ||||
|         "generated-artifacts/TestMixinStakingPool.json", | ||||
|         "generated-artifacts/TestProtocolFees.json", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user