protocol/contracts/staking/test/unit_tests/mixin_cumulative_rewards_test.ts
Xianny f0d7d10fe7
update abi-gen with new method interfaces (#2325)
* update abi-gen with new method interfaces

* wip: get all packages to build

* wip: get all packages to build

* Fix two contract wrapper calls

* Export necessary types part of the contract wrapper public interfaces

* Revive and fix wrapper_unit_tests

* Remove duplicate type

* Fix lib_exchange_rich_error_decoder tests

* Fix remaining test failures in contracts-* packages

* Prettier fixes

* remove transactionHelper

* lint and update changelogs

* Fix prettier

* Revert changes to reference docs

* Add back changelog already published and add revert changelog entry

* Add missing CHANGELOG entries

* Add missing comma

* Update mesh-rpc-client dep

* Update Mesh RPC logic in @0x/orderbook to v6.0.1-beta

* Align package versions
2019-11-14 11:22:29 -05:00

236 lines
11 KiB
TypeScript

import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants as stakingConstants } from '../../src/constants';
import { toBaseUnitAmount } from '../utils/number_utils';
import { artifacts } from '../artifacts';
import { TestMixinCumulativeRewardsContract } from '../wrappers';
blockchainTests.resets('MixinCumulativeRewards unit tests', env => {
const ZERO = new BigNumber(0);
const testRewards = [
{
numerator: new BigNumber(1),
denominator: new BigNumber(2),
},
{
numerator: new BigNumber(3),
denominator: new BigNumber(4),
},
];
const sumOfTestRewardsNormalized = {
numerator: new BigNumber(10),
denominator: new BigNumber(8),
};
let testPoolId: string;
let testContract: TestMixinCumulativeRewardsContract;
before(async () => {
// Deploy contracts
testContract = await TestMixinCumulativeRewardsContract.deployFrom0xArtifactAsync(
artifacts.TestMixinCumulativeRewards,
env.provider,
env.txDefaults,
artifacts,
stakingConstants.NIL_ADDRESS,
stakingConstants.NIL_ADDRESS,
);
// Create a test pool
const operatorShare = new BigNumber(1);
const addOperatorAsMaker = true;
const txReceipt = await testContract
.createStakingPool(operatorShare, addOperatorAsMaker)
.awaitTransactionSuccessAsync();
const createStakingPoolLog = txReceipt.logs[0];
testPoolId = (createStakingPoolLog as any).args.poolId;
});
describe('_isCumulativeRewardSet', () => {
it('Should return true iff denominator is non-zero', async () => {
const isSet = await testContract
.isCumulativeRewardSet({
numerator: ZERO,
denominator: new BigNumber(1),
})
.callAsync();
expect(isSet).to.be.true();
});
it('Should return false iff denominator is zero', async () => {
const isSet = await testContract
.isCumulativeRewardSet({
numerator: new BigNumber(1),
denominator: ZERO,
})
.callAsync();
expect(isSet).to.be.false();
});
});
describe('_addCumulativeReward', () => {
it('Should set value to `reward/stake` if this is the first cumulative reward', async () => {
await testContract
.addCumulativeReward(testPoolId, testRewards[0].numerator, testRewards[0].denominator)
.awaitTransactionSuccessAsync();
const mostRecentCumulativeReward = await testContract.getMostRecentCumulativeReward(testPoolId).callAsync();
expect(mostRecentCumulativeReward).to.deep.equal(testRewards[0]);
});
it('Should do nothing if a cumulative reward has already been recorded in the current epoch (`lastStoredEpoch == currentEpoch_`)', async () => {
await testContract
.addCumulativeReward(testPoolId, testRewards[0].numerator, testRewards[0].denominator)
.awaitTransactionSuccessAsync();
// this call should not overwrite existing value (testRewards[0])
await testContract
.addCumulativeReward(testPoolId, testRewards[1].numerator, testRewards[1].denominator)
.awaitTransactionSuccessAsync();
const mostRecentCumulativeReward = await testContract.getMostRecentCumulativeReward(testPoolId).callAsync();
expect(mostRecentCumulativeReward).to.deep.equal(testRewards[0]);
});
it('Should set value to normalized sum of `reward/stake` plus most recent cumulative reward, given one exists', async () => {
await testContract
.addCumulativeReward(testPoolId, testRewards[0].numerator, testRewards[0].denominator)
.awaitTransactionSuccessAsync();
await testContract.incrementEpoch().awaitTransactionSuccessAsync();
await testContract
.addCumulativeReward(testPoolId, testRewards[1].numerator, testRewards[1].denominator)
.awaitTransactionSuccessAsync();
const mostRecentCumulativeReward = await testContract.getMostRecentCumulativeReward(testPoolId).callAsync();
expect(mostRecentCumulativeReward).to.deep.equal(sumOfTestRewardsNormalized);
});
});
describe('_updateCumulativeReward', () => {
it('Should set current cumulative reward to most recent cumulative reward', async () => {
await testContract
.addCumulativeReward(testPoolId, testRewards[0].numerator, testRewards[0].denominator)
.awaitTransactionSuccessAsync();
await testContract.incrementEpoch().awaitTransactionSuccessAsync();
await testContract.updateCumulativeReward(testPoolId).awaitTransactionSuccessAsync();
const epoch = new BigNumber(2);
const mostRecentCumulativeReward = await testContract
.getCumulativeRewardAtEpochRaw(testPoolId, epoch)
.callAsync();
expect(mostRecentCumulativeReward).to.deep.equal(testRewards[0]);
});
});
describe('_computeMemberRewardOverInterval', () => {
const runTest = async (
amountToStake: BigNumber,
epochOfFirstReward: BigNumber,
epochOfSecondReward: BigNumber,
epochOfIntervalStart: BigNumber,
epochOfIntervalEnd: BigNumber,
): Promise<void> => {
// Simulate earning reward
await testContract
.storeCumulativeReward(testPoolId, testRewards[0], epochOfFirstReward)
.awaitTransactionSuccessAsync();
await testContract
.storeCumulativeReward(testPoolId, sumOfTestRewardsNormalized, epochOfSecondReward)
.awaitTransactionSuccessAsync();
const reward = await testContract
.computeMemberRewardOverInterval(testPoolId, amountToStake, epochOfIntervalStart, epochOfIntervalEnd)
.callAsync();
// Compute expected reward
const lhs = sumOfTestRewardsNormalized.numerator.dividedBy(sumOfTestRewardsNormalized.denominator);
const rhs = testRewards[0].numerator.dividedBy(testRewards[0].denominator);
const expectedReward = lhs.minus(rhs).multipliedBy(amountToStake);
// Assert correctness
expect(reward).to.bignumber.equal(expectedReward);
};
it('Should successfully compute reward over a valid interval when staking non-zero ZRX', async () => {
const amountToStake = toBaseUnitAmount(1);
const epochOfFirstReward = new BigNumber(1);
const epochOfSecondReward = new BigNumber(2);
const epochOfIntervalStart = new BigNumber(1);
const epochOfIntervalEnd = new BigNumber(2);
await runTest(
amountToStake,
epochOfFirstReward,
epochOfSecondReward,
epochOfIntervalStart,
epochOfIntervalEnd,
);
});
it('Should successfully compute reward if no entry for current epoch, but there is an entry for epoch n-1', async () => {
// End epoch = n-1 forces the code to query the previous epoch's cumulative reward
const amountToStake = toBaseUnitAmount(1);
const epochOfFirstReward = new BigNumber(1);
const epochOfSecondReward = new BigNumber(2);
const epochOfIntervalStart = new BigNumber(1);
const epochOfIntervalEnd = new BigNumber(3);
await runTest(
amountToStake,
epochOfFirstReward,
epochOfSecondReward,
epochOfIntervalStart,
epochOfIntervalEnd,
);
});
it('Should successfully compute reward if no entry for current epoch, but there is an entry for epoch n-2', async () => {
// End epoch = n-2 forces the code to query the most recent cumulative reward
const amountToStake = toBaseUnitAmount(1);
const epochOfFirstReward = new BigNumber(1);
const epochOfSecondReward = new BigNumber(2);
const epochOfIntervalStart = new BigNumber(1);
const epochOfIntervalEnd = new BigNumber(4);
await runTest(
amountToStake,
epochOfFirstReward,
epochOfSecondReward,
epochOfIntervalStart,
epochOfIntervalEnd,
);
});
it('Should successfully compute reward are no cumulative reward entries', async () => {
// No entries forces the default cumulatie reward to be used in computations
const stake = toBaseUnitAmount(1);
const beginEpoch = new BigNumber(1);
const endEpoch = new BigNumber(2);
const reward = await testContract
.computeMemberRewardOverInterval(testPoolId, stake, beginEpoch, endEpoch)
.callAsync();
expect(reward).to.bignumber.equal(ZERO);
});
it('Should return zero if no stake was delegated', async () => {
const stake = toBaseUnitAmount(0);
const beginEpoch = new BigNumber(1);
const endEpoch = new BigNumber(2);
const reward = await testContract
.computeMemberRewardOverInterval(testPoolId, stake, beginEpoch, endEpoch)
.callAsync();
expect(reward).to.bignumber.equal(ZERO);
});
it('Should return zero if the start/end of the interval are the same epoch', async () => {
const stake = toBaseUnitAmount(1);
const beginEpoch = new BigNumber(1);
const endEpoch = new BigNumber(1);
const reward = await testContract
.computeMemberRewardOverInterval(testPoolId, stake, beginEpoch, endEpoch)
.callAsync();
expect(reward).to.bignumber.equal(ZERO);
});
it('Should revert if start is greater than the end of the interval', async () => {
const stake = toBaseUnitAmount(1);
const beginEpoch = new BigNumber(2);
const endEpoch = new BigNumber(1);
const tx = testContract
.computeMemberRewardOverInterval(testPoolId, stake, beginEpoch, endEpoch)
.callAsync();
return expect(tx).to.revertWith('CR_INTERVAL_INVALID');
});
});
});