@0x/contracts-staking: More MixinFinalizer unit tests.

This commit is contained in:
Lawrence Forman 2019-09-14 05:34:40 -04:00 committed by Lawrence Forman
parent f5ab1e6f86
commit 03c59fdaf7
3 changed files with 139 additions and 6 deletions

View File

@ -342,7 +342,7 @@ contract MixinFinalizer is
pool.weightedStake,
unfinalizedTotalWeightedStake,
cobbDouglasAlphaNumerator,
cobbDouglasAlphaDenomintor
cobbDouglasAlphaDenominator
);
// Split the reward between the operator and delegators.

View File

@ -20,6 +20,7 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/interfaces/IStructs.sol";
import "../src/libs/LibCobbDouglas.sol";
import "../src/Staking.sol";
@ -102,6 +103,29 @@ contract TestFinalizer is
numActivePoolsThisEpoch += 1;
}
/// @dev Compute Cobb-Douglas.
function cobbDouglas(
uint256 totalRewards,
uint256 ownerFees,
uint256 totalFees,
uint256 ownerStake,
uint256 totalStake
)
external
view
returns (uint256 ownerRewards)
{
ownerRewards = LibCobbDouglas._cobbDouglas(
totalRewards,
ownerFees,
totalFees,
ownerStake,
totalStake,
cobbDouglasAlphaNumerator,
cobbDouglasAlphaDenominator
);
}
/// @dev Expose `_getUnfinalizedPoolReward()`
function internalGetUnfinalizedPoolRewards(bytes32 poolId)
external

View File

@ -63,7 +63,7 @@ blockchainTests.resets.only('finalization tests', env => {
async function addActivePoolAsync(opts?: Partial<ActivePoolOpts>): Promise<ActivePoolOpts> {
const _opts = {
poolId: hexRandom(),
operatorShare: Math.random(),
operatorShare: Math.floor(Math.random() * constants.PPM_DENOMINATOR) / constants.PPM_DENOMINATOR,
feesCollected: getRandomInteger(0, ONE_ETHER),
membersStake: getRandomInteger(0, ONE_ETHER),
weightedStake: getRandomInteger(0, ONE_ETHER),
@ -445,16 +445,42 @@ blockchainTests.resets.only('finalization tests', env => {
function assertPoolRewards(actual: PoolRewards, expected: Partial<PoolRewards>): void {
if (expected.operatorReward !== undefined) {
expect(actual.operatorReward).to.bignumber.eq(actual.operatorReward);
expect(actual.operatorReward).to.bignumber.eq(expected.operatorReward);
}
if (expected.membersReward !== undefined) {
expect(actual.membersReward).to.bignumber.eq(actual.membersReward);
expect(actual.membersReward).to.bignumber.eq(expected.membersReward);
}
if (expected.membersStake !== undefined) {
expect(actual.membersStake).to.bignumber.eq(actual.membersStake);
expect(actual.membersStake).to.bignumber.eq(expected.membersStake);
}
}
async function callCobbDouglasAsync(
totalRewards: Numberish,
fees: Numberish,
totalFees: Numberish,
stake: Numberish,
totalStake: Numberish,
): Promise<BigNumber> {
return testContract.cobbDouglas.callAsync(
new BigNumber(totalRewards),
new BigNumber(fees),
new BigNumber(totalFees),
new BigNumber(stake),
new BigNumber(totalStake),
);
}
function splitRewards(pool: ActivePoolOpts, totalReward: Numberish): [BigNumber, BigNumber] {
if (new BigNumber(pool.membersStake).isZero()) {
return [new BigNumber(totalReward), ZERO_AMOUNT];
}
const operatorShare = new BigNumber(totalReward)
.times(pool.operatorShare).integerValue(BigNumber.ROUND_DOWN);
const membersShare = new BigNumber(totalReward).minus(operatorShare);
return [operatorShare, membersShare];
}
describe('_getUnfinalizedPoolReward()', () => {
const ZERO_REWARDS = {
operatorReward: 0,
@ -477,6 +503,13 @@ blockchainTests.resets.only('finalization tests', env => {
assertPoolRewards(rewards, ZERO_REWARDS);
});
it('returns empty if pool is active only in the current epoch', async () => {
const pool = await addActivePoolAsync();
const rewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(rewards, ZERO_REWARDS);
});
it('returns empty if pool was only active in the 2 epochs ago', async () => {
const pool = await addActivePoolAsync();
await testContract.endEpoch.awaitTransactionSuccessAsync();
@ -488,12 +521,88 @@ blockchainTests.resets.only('finalization tests', env => {
it('returns empty if pool was already finalized', async () => {
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
const pool = _.sample(pools) as ActivePoolOpts;
const [pool] = _.sampleSize(pools, 1);
await testContract.endEpoch.awaitTransactionSuccessAsync();
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
const rewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(rewards, ZERO_REWARDS);
});
it('computes one reward among one pool', async () => {
const pool = await addActivePoolAsync();
await testContract.endEpoch.awaitTransactionSuccessAsync();
const expectedTotalRewards = INITIAL_BALANCE;
const [expectedOperatorReward, expectedMembersReward] =
splitRewards(pool, expectedTotalRewards);
const actualRewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(
actualRewards,
{
operatorReward: expectedOperatorReward,
membersReward: expectedMembersReward,
membersStake: pool.membersStake,
},
);
});
it('computes one reward among multiple pools', async () => {
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
await testContract.endEpoch.awaitTransactionSuccessAsync();
const [pool] = _.sampleSize(pools, 1);
const totalFeesCollected = BigNumber.sum(...pools.map(p => p.feesCollected));
const totalWeightedStake = BigNumber.sum(...pools.map(p => p.weightedStake));
const expectedTotalRewards = await callCobbDouglasAsync(
INITIAL_BALANCE,
pool.feesCollected,
totalFeesCollected,
pool.weightedStake,
totalWeightedStake,
);
const [expectedOperatorReward, expectedMembersReward] =
splitRewards(pool, expectedTotalRewards);
const actualRewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(
actualRewards,
{
operatorReward: expectedOperatorReward,
membersReward: expectedMembersReward,
membersStake: pool.membersStake,
},
);
});
it('computes a reward with 0% operatorShare', async () => {
const pool = await addActivePoolAsync({ operatorShare: 0 });
await testContract.endEpoch.awaitTransactionSuccessAsync();
const actualRewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(
actualRewards,
{
operatorReward: 0,
membersReward: INITIAL_BALANCE,
membersStake: pool.membersStake,
},
);
});
it('computes a reward with 100% operatorShare', async () => {
const pool = await addActivePoolAsync({ operatorShare: 1 });
await testContract.endEpoch.awaitTransactionSuccessAsync();
const actualRewards = await testContract
.internalGetUnfinalizedPoolRewards.callAsync(pool.poolId);
assertPoolRewards(
actualRewards,
{
operatorReward: INITIAL_BALANCE,
membersReward: 0,
membersStake: pool.membersStake,
},
);
});
});
});
// tslint:disable: max-file-line-count