@0x/contracts-staking
: All tests passing!
This commit is contained in:
parent
527ec28915
commit
a43b494302
@ -57,6 +57,10 @@
|
||||
{
|
||||
"note": "Refactored out `_cobbDouglas()` into its own library",
|
||||
"pr": 2179
|
||||
},
|
||||
{
|
||||
"note": "Introduce multi-block finalization.",
|
||||
"pr": 2155
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -271,29 +271,12 @@ contract MixinStakingPoolRewards is
|
||||
return reward = 0;
|
||||
}
|
||||
|
||||
// From here we know:
|
||||
// 1. `currentEpoch > 0`
|
||||
// 2. `stake.currentEpoch < currentEpoch`.
|
||||
|
||||
// Get the last epoch where a reward was credited to this pool.
|
||||
uint256 lastRewardEpoch = lastPoolRewardEpoch[poolId];
|
||||
// Get the last reward epoch for which we collected rewards from.
|
||||
uint256 lastCollectedRewardEpoch =
|
||||
lastCollectedRewardsEpochToPoolByOwner[member][poolId];
|
||||
|
||||
// If either of these are true, the most recent reward has already been
|
||||
// claimed.
|
||||
if (lastCollectedRewardEpoch == lastRewardEpoch
|
||||
|| stake.currentEpoch >= lastRewardEpoch) {
|
||||
return reward = 0;
|
||||
}
|
||||
|
||||
// If there are unfinalized rewards this epoch, compute the member's
|
||||
// share.
|
||||
if (unfinalizedMembersReward != 0 && unfinalizedDelegatedStake != 0) {
|
||||
// Unfinalized rewards are always earned from stake in
|
||||
// the prior epoch so we want the stake at `currentEpoch-1`.
|
||||
uint256 _stake = stake.currentEpoch == currentEpoch - 1 ?
|
||||
uint256 _stake = stake.currentEpoch >= currentEpoch - 1 ?
|
||||
stake.currentEpochBalance :
|
||||
stake.nextEpochBalance;
|
||||
if (_stake != 0) {
|
||||
@ -303,16 +286,35 @@ contract MixinStakingPoolRewards is
|
||||
}
|
||||
}
|
||||
|
||||
// Add rewards up to the last reward epoch.
|
||||
if (lastRewardEpoch != 0) {
|
||||
// Get the last epoch where a reward was credited to this pool.
|
||||
uint256 lastRewardEpoch = lastPoolRewardEpoch[poolId];
|
||||
|
||||
// If the stake has been touched since the last reward epoch,
|
||||
// it has already been claimed.
|
||||
if (stake.currentEpoch >= lastRewardEpoch) {
|
||||
return reward;
|
||||
}
|
||||
// From here we know: `stake.currentEpoch < currentEpoch > 0`.
|
||||
|
||||
if (stake.currentEpoch < lastRewardEpoch) {
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
stake,
|
||||
stake.currentEpoch + 1,
|
||||
lastRewardEpoch
|
||||
stake.currentEpoch,
|
||||
stake.currentEpoch + 1
|
||||
)
|
||||
);
|
||||
if (stake.currentEpoch + 1 < lastRewardEpoch) {
|
||||
reward = reward.safeAdd(
|
||||
_computeMemberRewardOverInterval(
|
||||
poolId,
|
||||
stake,
|
||||
stake.currentEpoch + 1,
|
||||
lastRewardEpoch
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,5 +358,9 @@ contract MixinStakingPoolRewards is
|
||||
isDependent
|
||||
);
|
||||
}
|
||||
uint256 nextEpoch = epoch.safeAdd(1);
|
||||
if (!_isCumulativeRewardSet(cumulativeRewardsByPoolPtr[nextEpoch])) {
|
||||
cumulativeRewardsByPoolPtr[nextEpoch] = mostRecentCumulativeRewards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,14 +165,11 @@ contract MixinFinalizer is
|
||||
poolsRemaining = poolsRemaining.safeSub(1);
|
||||
}
|
||||
|
||||
// Deposit all the rewards at once into the RewardVault.
|
||||
if (rewardsPaid != 0) {
|
||||
_depositIntoStakingPoolRewardVault(rewardsPaid);
|
||||
}
|
||||
|
||||
// Update finalization states.
|
||||
totalRewardsPaidLastEpoch =
|
||||
totalRewardsPaidLastEpoch.safeAdd(rewardsPaid);
|
||||
if (rewardsPaid != 0) {
|
||||
totalRewardsPaidLastEpoch =
|
||||
totalRewardsPaidLastEpoch.safeAdd(rewardsPaid);
|
||||
}
|
||||
unfinalizedPoolsRemaining = _unfinalizedPoolsRemaining = poolsRemaining;
|
||||
|
||||
// If there are no more unfinalized pools remaining, the epoch is
|
||||
@ -184,6 +181,12 @@ contract MixinFinalizer is
|
||||
unfinalizedRewardsAvailable.safeSub(totalRewardsPaidLastEpoch)
|
||||
);
|
||||
}
|
||||
|
||||
// Deposit all the rewards at once into the RewardVault.
|
||||
if (rewardsPaid != 0) {
|
||||
_depositIntoStakingPoolRewardVault(rewardsPaid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @dev Instantly finalizes a single pool that was active in the previous
|
||||
|
@ -43,42 +43,33 @@ export class FinalizerActor extends BaseActor {
|
||||
|
||||
public async finalizeAsync(rewards: Reward[] = []): Promise<void> {
|
||||
// cache initial info and balances
|
||||
const operatorShareByPoolId =
|
||||
await this._getOperatorShareByPoolIdAsync(this._poolIds);
|
||||
const rewardVaultBalanceByPoolId =
|
||||
await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
||||
const delegatorBalancesByPoolId =
|
||||
await this._getDelegatorBalancesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
const delegatorStakesByPoolId =
|
||||
await this._getDelegatorStakesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
const operatorShareByPoolId = await this._getOperatorShareByPoolIdAsync(this._poolIds);
|
||||
const rewardVaultBalanceByPoolId = await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
||||
const delegatorBalancesByPoolId = await this._getDelegatorBalancesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
const delegatorStakesByPoolId = await this._getDelegatorStakesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
// compute expected changes
|
||||
const expectedRewardVaultBalanceByPoolId =
|
||||
await this._computeExpectedRewardVaultBalanceAsyncByPoolIdAsync(
|
||||
rewards,
|
||||
rewardVaultBalanceByPoolId,
|
||||
operatorShareByPoolId,
|
||||
);
|
||||
const totalRewardsByPoolId =
|
||||
_.zipObject(_.map(rewards, 'poolId'), _.map(rewards, 'reward'));
|
||||
const expectedDelegatorBalancesByPoolId =
|
||||
await this._computeExpectedDelegatorBalancesByPoolIdAsync(
|
||||
this._delegatorsByPoolId,
|
||||
delegatorBalancesByPoolId,
|
||||
delegatorStakesByPoolId,
|
||||
operatorShareByPoolId,
|
||||
totalRewardsByPoolId,
|
||||
);
|
||||
const expectedRewardVaultBalanceByPoolId = await this._computeExpectedRewardVaultBalanceAsyncByPoolIdAsync(
|
||||
rewards,
|
||||
rewardVaultBalanceByPoolId,
|
||||
operatorShareByPoolId,
|
||||
);
|
||||
const totalRewardsByPoolId = _.zipObject(_.map(rewards, 'poolId'), _.map(rewards, 'reward'));
|
||||
const expectedDelegatorBalancesByPoolId = await this._computeExpectedDelegatorBalancesByPoolIdAsync(
|
||||
this._delegatorsByPoolId,
|
||||
delegatorBalancesByPoolId,
|
||||
delegatorStakesByPoolId,
|
||||
operatorShareByPoolId,
|
||||
totalRewardsByPoolId,
|
||||
);
|
||||
// finalize
|
||||
await this._stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||
// assert reward vault changes
|
||||
const finalRewardVaultBalanceByPoolId =
|
||||
await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
||||
const finalRewardVaultBalanceByPoolId = await this._getRewardVaultBalanceByPoolIdAsync(this._poolIds);
|
||||
expect(finalRewardVaultBalanceByPoolId, 'final pool balances in reward vault').to.be.deep.equal(
|
||||
expectedRewardVaultBalanceByPoolId,
|
||||
);
|
||||
// assert delegator balances
|
||||
const finalDelegatorBalancesByPoolId =
|
||||
await this._getDelegatorBalancesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
const finalDelegatorBalancesByPoolId = await this._getDelegatorBalancesByPoolIdAsync(this._delegatorsByPoolId);
|
||||
expect(finalDelegatorBalancesByPoolId, 'final delegator balances in reward vault').to.be.deep.equal(
|
||||
expectedDelegatorBalancesByPoolId,
|
||||
);
|
||||
@ -103,13 +94,12 @@ export class FinalizerActor extends BaseActor {
|
||||
}
|
||||
|
||||
const operator = this._operatorByPoolId[poolId];
|
||||
const [, membersStakeInPool] =
|
||||
await this._getOperatorAndDelegatorsStakeInPoolAsync(poolId);
|
||||
const [, membersStakeInPool] = await this._getOperatorAndDelegatorsStakeInPoolAsync(poolId);
|
||||
const operatorShare = operatorShareByPoolId[poolId].dividedBy(PPM_100_PERCENT);
|
||||
const totalReward = totalRewardByPoolId[poolId];
|
||||
const operatorReward = membersStakeInPool.eq(0) ?
|
||||
totalReward :
|
||||
totalReward.times(operatorShare).integerValue(BigNumber.ROUND_DOWN);
|
||||
const operatorReward = membersStakeInPool.eq(0)
|
||||
? totalReward
|
||||
: totalReward.times(operatorShare).integerValue(BigNumber.ROUND_DOWN);
|
||||
const membersTotalReward = totalReward.minus(operatorReward);
|
||||
|
||||
for (const delegator of delegatorsByPoolId[poolId]) {
|
||||
@ -133,10 +123,8 @@ export class FinalizerActor extends BaseActor {
|
||||
private async _getDelegatorBalancesByPoolIdAsync(
|
||||
delegatorsByPoolId: DelegatorsByPoolId,
|
||||
): Promise<DelegatorBalancesByPoolId> {
|
||||
const computeRewardBalanceOfDelegator =
|
||||
this._stakingApiWrapper.stakingContract.computeRewardBalanceOfDelegator;
|
||||
const rewardVaultBalanceOfOperator =
|
||||
this._stakingApiWrapper.rewardVaultContract.balanceOfOperator;
|
||||
const computeRewardBalanceOfDelegator = this._stakingApiWrapper.stakingContract.computeRewardBalanceOfDelegator;
|
||||
const rewardVaultBalanceOfOperator = this._stakingApiWrapper.rewardVaultContract.balanceOfOperator;
|
||||
const delegatorBalancesByPoolId: DelegatorBalancesByPoolId = {};
|
||||
|
||||
for (const poolId of Object.keys(delegatorsByPoolId)) {
|
||||
@ -144,19 +132,11 @@ export class FinalizerActor extends BaseActor {
|
||||
const delegators = delegatorsByPoolId[poolId];
|
||||
delegatorBalancesByPoolId[poolId] = {};
|
||||
for (const delegator of delegators) {
|
||||
let balance =
|
||||
new BigNumber(delegatorBalancesByPoolId[poolId][delegator] || 0);
|
||||
let balance = new BigNumber(delegatorBalancesByPoolId[poolId][delegator] || 0);
|
||||
if (delegator === operator) {
|
||||
balance = balance.plus(
|
||||
await rewardVaultBalanceOfOperator.callAsync(poolId),
|
||||
);
|
||||
balance = balance.plus(await rewardVaultBalanceOfOperator.callAsync(poolId));
|
||||
} else {
|
||||
balance = balance.plus(
|
||||
await computeRewardBalanceOfDelegator.callAsync(
|
||||
poolId,
|
||||
delegator,
|
||||
),
|
||||
);
|
||||
balance = balance.plus(await computeRewardBalanceOfDelegator.callAsync(poolId, delegator));
|
||||
}
|
||||
delegatorBalancesByPoolId[poolId][delegator] = balance;
|
||||
}
|
||||
@ -167,16 +147,13 @@ export class FinalizerActor extends BaseActor {
|
||||
private async _getDelegatorStakesByPoolIdAsync(
|
||||
delegatorsByPoolId: DelegatorsByPoolId,
|
||||
): Promise<DelegatorBalancesByPoolId> {
|
||||
const getStakeDelegatedToPoolByOwner =
|
||||
this._stakingApiWrapper.stakingContract.getStakeDelegatedToPoolByOwner;
|
||||
const getStakeDelegatedToPoolByOwner = this._stakingApiWrapper.stakingContract.getStakeDelegatedToPoolByOwner;
|
||||
const delegatorBalancesByPoolId: DelegatorBalancesByPoolId = {};
|
||||
for (const poolId of Object.keys(delegatorsByPoolId)) {
|
||||
const delegators = delegatorsByPoolId[poolId];
|
||||
delegatorBalancesByPoolId[poolId] = {};
|
||||
for (const delegator of delegators) {
|
||||
delegatorBalancesByPoolId[poolId][
|
||||
delegator
|
||||
] = (await getStakeDelegatedToPoolByOwner.callAsync(
|
||||
delegatorBalancesByPoolId[poolId][delegator] = (await getStakeDelegatedToPoolByOwner.callAsync(
|
||||
delegator,
|
||||
poolId,
|
||||
)).currentEpochBalance;
|
||||
@ -195,13 +172,12 @@ export class FinalizerActor extends BaseActor {
|
||||
const expectedRewardVaultBalanceByPoolId = _.cloneDeep(rewardVaultBalanceByPoolId);
|
||||
for (const reward of rewards) {
|
||||
const operatorShare = operatorShareByPoolId[reward.poolId];
|
||||
expectedRewardVaultBalanceByPoolId[reward.poolId] =
|
||||
await this._computeExpectedRewardVaultBalanceAsync(
|
||||
reward.poolId,
|
||||
reward.reward,
|
||||
expectedRewardVaultBalanceByPoolId[reward.poolId],
|
||||
operatorShare,
|
||||
);
|
||||
expectedRewardVaultBalanceByPoolId[reward.poolId] = await this._computeExpectedRewardVaultBalanceAsync(
|
||||
reward.poolId,
|
||||
reward.reward,
|
||||
expectedRewardVaultBalanceByPoolId[reward.poolId],
|
||||
operatorShare,
|
||||
);
|
||||
}
|
||||
return [expectedOperatorBalanceByPoolId, expectedRewardVaultBalanceByPoolId];
|
||||
}
|
||||
@ -233,18 +209,13 @@ export class FinalizerActor extends BaseActor {
|
||||
return operatorBalanceByPoolId;
|
||||
}
|
||||
|
||||
private async _getOperatorAndDelegatorsStakeInPoolAsync(
|
||||
poolId: string,
|
||||
): Promise<[BigNumber, BigNumber]> {
|
||||
private async _getOperatorAndDelegatorsStakeInPoolAsync(poolId: string): Promise<[BigNumber, BigNumber]> {
|
||||
const stakingContract = this._stakingApiWrapper.stakingContract;
|
||||
const operator = await stakingContract.getPoolOperator.callAsync(poolId);
|
||||
const totalStakeInPool = (await stakingContract.getTotalStakeDelegatedToPool.callAsync(
|
||||
poolId,
|
||||
)).currentEpochBalance;
|
||||
const operatorStakeInPool = (await stakingContract.getStakeDelegatedToPoolByOwner.callAsync(
|
||||
operator,
|
||||
poolId,
|
||||
)).currentEpochBalance;
|
||||
const totalStakeInPool = (await stakingContract.getTotalStakeDelegatedToPool.callAsync(poolId))
|
||||
.currentEpochBalance;
|
||||
const operatorStakeInPool = (await stakingContract.getStakeDelegatedToPoolByOwner.callAsync(operator, poolId))
|
||||
.currentEpochBalance;
|
||||
const membersStakeInPool = totalStakeInPool.minus(operatorStakeInPool);
|
||||
return [operatorStakeInPool, membersStakeInPool];
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
||||
// Skip to next epoch so operator stake is realized.
|
||||
await stakingApiWrapper.utils.skipToNextEpochAndFinalizeAsync();
|
||||
});
|
||||
describe.skip('Reward Simulation', () => {
|
||||
describe('Reward Simulation', () => {
|
||||
interface EndBalances {
|
||||
// staker 1
|
||||
stakerRewardVaultBalance_1?: BigNumber;
|
||||
@ -494,7 +494,7 @@ blockchainTests.resets('Testing Rewards', env => {
|
||||
operatorEthVaultBalance: totalRewardsNotForDelegator,
|
||||
});
|
||||
});
|
||||
it.only('Should collect fees correctly when leaving and returning to a pool', async () => {
|
||||
it('Should collect fees correctly when leaving and returning to a pool', async () => {
|
||||
// first staker delegates (epoch 0)
|
||||
const rewardsForDelegator = [toBaseUnitAmount(10), toBaseUnitAmount(15)];
|
||||
const rewardNotForDelegator = toBaseUnitAmount(7);
|
||||
@ -628,13 +628,13 @@ blockchainTests.resets('Testing Rewards', env => {
|
||||
for (const [staker, stakeAmount] of stakersAndStake) {
|
||||
await staker.stakeWithPoolAsync(poolId, stakeAmount);
|
||||
}
|
||||
// skip epoch, so staker can start earning rewards
|
||||
// skip epoch, so stakers can start earning rewards
|
||||
await payProtocolFeeAndFinalize();
|
||||
// finalize
|
||||
const reward = toBaseUnitAmount(10);
|
||||
await payProtocolFeeAndFinalize(reward);
|
||||
// Undelegate 0 stake to move rewards from RewardVault into the EthVault.
|
||||
for (const [staker] of stakersAndStake) {
|
||||
for (const [staker] of _.reverse(stakersAndStake)) {
|
||||
await staker.moveStakeAsync(
|
||||
new StakeInfo(StakeStatus.Delegated, poolId),
|
||||
new StakeInfo(StakeStatus.Active),
|
||||
|
@ -36,9 +36,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
stake: Numberish;
|
||||
}
|
||||
|
||||
async function rewardPoolMembersAsync(
|
||||
opts?: Partial<RewardPoolMembersOpts>,
|
||||
): Promise<RewardPoolMembersOpts> {
|
||||
async function rewardPoolMembersAsync(opts?: Partial<RewardPoolMembersOpts>): Promise<RewardPoolMembersOpts> {
|
||||
const _opts = {
|
||||
poolId: hexRandom(),
|
||||
reward: getRandomInteger(1, toBaseUnitAmount(100)),
|
||||
@ -103,11 +101,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
...opts,
|
||||
};
|
||||
const fn = now ? testContract.delegateStakeNow : testContract.delegateStake;
|
||||
const receipt = await fn.awaitTransactionSuccessAsync(
|
||||
_opts.delegator,
|
||||
poolId,
|
||||
new BigNumber(_opts.stake),
|
||||
);
|
||||
const receipt = await fn.awaitTransactionSuccessAsync(_opts.delegator, poolId, new BigNumber(_opts.stake));
|
||||
return {
|
||||
..._opts,
|
||||
deposit: getDepositFromLogs(receipt.logs, poolId, _opts.delegator),
|
||||
@ -120,17 +114,10 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
stake?: Numberish,
|
||||
): Promise<ResultWithDeposit<{ stake: BigNumber }>> {
|
||||
const _stake = new BigNumber(
|
||||
stake || (await
|
||||
testContract
|
||||
.getStakeDelegatedToPoolByOwner
|
||||
.callAsync(delegator, poolId)
|
||||
).currentEpochBalance,
|
||||
);
|
||||
const receipt = await testContract.undelegateStake.awaitTransactionSuccessAsync(
|
||||
delegator,
|
||||
poolId,
|
||||
_stake,
|
||||
stake ||
|
||||
(await testContract.getStakeDelegatedToPoolByOwner.callAsync(delegator, poolId)).currentEpochBalance,
|
||||
);
|
||||
const receipt = await testContract.undelegateStake.awaitTransactionSuccessAsync(delegator, poolId, _stake);
|
||||
return {
|
||||
stake: _stake,
|
||||
deposit: getDepositFromLogs(receipt.logs, poolId, delegator),
|
||||
@ -138,11 +125,10 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
}
|
||||
|
||||
function getDepositFromLogs(logs: LogEntry[], poolId: string, delegator?: string): BigNumber {
|
||||
const events =
|
||||
filterLogsToArguments<TestDelegatorRewardsDepositEventArgs>(
|
||||
logs,
|
||||
TestDelegatorRewardsEvents.Deposit,
|
||||
);
|
||||
const events = filterLogsToArguments<TestDelegatorRewardsDepositEventArgs>(
|
||||
logs,
|
||||
TestDelegatorRewardsEvents.Deposit,
|
||||
);
|
||||
if (events.length > 0) {
|
||||
expect(events.length).to.eq(1);
|
||||
expect(events[0].poolId).to.eq(poolId);
|
||||
@ -160,11 +146,8 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
return epoch.toNumber();
|
||||
}
|
||||
|
||||
async function getDelegatorRewardAsync(poolId: string, delegator: string): Promise<BigNumber> {
|
||||
return testContract.computeRewardBalanceOfDelegator.callAsync(
|
||||
poolId,
|
||||
delegator,
|
||||
);
|
||||
async function getDelegatorRewardBalanceAsync(poolId: string, delegator: string): Promise<BigNumber> {
|
||||
return testContract.computeRewardBalanceOfDelegator.callAsync(poolId, delegator);
|
||||
}
|
||||
|
||||
async function touchStakeAsync(poolId: string, delegator: string): Promise<ResultWithDeposit<{}>> {
|
||||
@ -197,7 +180,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
it('nothing in epoch 0 for delegator with no stake', async () => {
|
||||
const { poolId } = await rewardPoolMembersAsync();
|
||||
const delegator = randomAddress();
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -205,7 +188,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
const { poolId } = await rewardPoolMembersAsync();
|
||||
const delegator = randomAddress();
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -214,7 +197,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
// Assign active stake to pool in epoch 0, which is usuaslly not
|
||||
// possible due to delegating delays.
|
||||
const { delegator } = await delegateStakeNowAsync(poolId);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -222,7 +205,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
const { poolId } = await rewardPoolMembersAsync();
|
||||
const { delegator } = await delegateStakeAsync(poolId);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -232,7 +215,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
// rewards paid for stake in epoch 0.
|
||||
await rewardPoolMembersAsync({ poolId, stake });
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -243,7 +226,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1.
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(reward);
|
||||
});
|
||||
|
||||
@ -255,7 +238,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(BigNumber.sum(reward1, reward2));
|
||||
});
|
||||
|
||||
@ -265,10 +248,11 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1.
|
||||
const { reward, stake: rewardStake } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: new BigNumber(delegatorStake).times(2) },
|
||||
);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const { reward, stake: rewardStake } = await rewardPoolMembersAsync({
|
||||
poolId,
|
||||
stake: new BigNumber(delegatorStake).times(2),
|
||||
});
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedDelegatorRewards = computeDelegatorRewards(reward, delegatorStake, rewardStake);
|
||||
assertRoughlyEquals(delegatorReward, expectedDelegatorRewards);
|
||||
});
|
||||
@ -279,12 +263,10 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1.
|
||||
const { reward } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake },
|
||||
);
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const { deposit } = await undelegateStakeAsync(poolId, delegator);
|
||||
expect(deposit).to.bignumber.eq(reward);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -294,13 +276,11 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1.
|
||||
const { reward } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake },
|
||||
);
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const { deposit } = await undelegateStakeAsync(poolId, delegator);
|
||||
expect(deposit).to.bignumber.eq(reward);
|
||||
await delegateStakeAsync(poolId, { delegator, stake });
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -316,14 +296,50 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
await advanceEpochAsync(); // epoch 4
|
||||
// rewards paid for stake in epoch 3.
|
||||
const { reward } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake },
|
||||
);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(reward);
|
||||
});
|
||||
|
||||
it.only('computes correct rewards for 2 staggered delegators', async () => {
|
||||
it('ignores rewards paid in the same epoch the stake was first active in', async () => {
|
||||
const poolId = hexRandom();
|
||||
// stake at 0
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
// Pay rewards for epoch 0.
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// Pay rewards for epoch 1.
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(delegatorReward).to.bignumber.eq(reward);
|
||||
});
|
||||
|
||||
it('uses old stake for rewards paid in the same epoch EXTRA stake was first active in', async () => {
|
||||
const poolId = hexRandom();
|
||||
// stake at 0
|
||||
const { delegator, stake: stake1 } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1 (stake1 now active)
|
||||
// add extra stake
|
||||
const { stake: stake2 } = await delegateStakeAsync(poolId, { delegator });
|
||||
const totalStake = BigNumber.sum(stake1, stake2);
|
||||
// Make the total stake in rewards > totalStake so delegator never
|
||||
// receives 100% of rewards.
|
||||
const rewardStake = totalStake.times(2);
|
||||
await advanceEpochAsync(); // epoch 2 (stake2 now active)
|
||||
// Pay rewards for epoch 1.
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
// Pay rewards for epoch 2.
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedDelegatorReward = BigNumber.sum(
|
||||
computeDelegatorRewards(reward1, stake1, rewardStake),
|
||||
computeDelegatorRewards(reward2, totalStake, rewardStake),
|
||||
);
|
||||
expect(delegatorReward).to.bignumber.eq(expectedDelegatorReward);
|
||||
});
|
||||
|
||||
it('computes correct rewards for 2 staggered delegators', async () => {
|
||||
const poolId = hexRandom();
|
||||
const { delegator: delegatorA, stake: stakeA } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1 (stake A now active)
|
||||
@ -331,24 +347,18 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const totalStake = BigNumber.sum(stakeA, stakeB);
|
||||
await advanceEpochAsync(); // epoch 2 (stake B now active)
|
||||
// rewards paid for stake in epoch 1 (delegator A only)
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: stakeA },
|
||||
);
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync({ poolId, stake: stakeA });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
// rewards paid for stake in epoch 2 (delegator A and B)
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: totalStake },
|
||||
);
|
||||
const delegatorRewardA = await getDelegatorRewardAsync(poolId, delegatorA);
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
const delegatorRewardA = await getDelegatorRewardBalanceAsync(poolId, delegatorA);
|
||||
const expectedDelegatorRewardA = BigNumber.sum(
|
||||
computeDelegatorRewards(reward1, stakeA, stakeA),
|
||||
computeDelegatorRewards(reward2, stakeA, totalStake),
|
||||
);
|
||||
assertRoughlyEquals(delegatorRewardA, expectedDelegatorRewardA);
|
||||
const delegatorRewardB = await getDelegatorRewardAsync(poolId, delegatorB);
|
||||
const expectedDelegatorRewardB = BigNumber.sum(
|
||||
computeDelegatorRewards(BigNumber.sum(reward1, reward2), stakeB, totalStake),
|
||||
);
|
||||
const delegatorRewardB = await getDelegatorRewardBalanceAsync(poolId, delegatorB);
|
||||
const expectedDelegatorRewardB = BigNumber.sum(computeDelegatorRewards(reward2, stakeB, totalStake));
|
||||
assertRoughlyEquals(delegatorRewardB, expectedDelegatorRewardB);
|
||||
});
|
||||
|
||||
@ -360,25 +370,19 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const totalStake = BigNumber.sum(stakeA, stakeB);
|
||||
await advanceEpochAsync(); // epoch 2 (stake B now active)
|
||||
// rewards paid for stake in epoch 1 (delegator A only)
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: stakeA },
|
||||
);
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync({ poolId, stake: stakeA });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
await advanceEpochAsync(); // epoch 4
|
||||
// rewards paid for stake in epoch 3 (delegator A and B)
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: totalStake },
|
||||
);
|
||||
const delegatorRewardA = await getDelegatorRewardAsync(poolId, delegatorA);
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
const delegatorRewardA = await getDelegatorRewardBalanceAsync(poolId, delegatorA);
|
||||
const expectedDelegatorRewardA = BigNumber.sum(
|
||||
computeDelegatorRewards(reward1, stakeA, stakeA),
|
||||
computeDelegatorRewards(reward2, stakeA, totalStake),
|
||||
);
|
||||
assertRoughlyEquals(delegatorRewardA, expectedDelegatorRewardA);
|
||||
const delegatorRewardB = await getDelegatorRewardAsync(poolId, delegatorB);
|
||||
const expectedDelegatorRewardB = BigNumber.sum(
|
||||
computeDelegatorRewards(reward2, stakeB, totalStake),
|
||||
);
|
||||
const delegatorRewardB = await getDelegatorRewardBalanceAsync(poolId, delegatorB);
|
||||
const expectedDelegatorRewardB = BigNumber.sum(computeDelegatorRewards(reward2, stakeB, totalStake));
|
||||
assertRoughlyEquals(delegatorRewardB, expectedDelegatorRewardB);
|
||||
});
|
||||
|
||||
@ -388,15 +392,17 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1.
|
||||
const { reward: reward1, stake: rewardStake1 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: new BigNumber(delegatorStake).times(2) },
|
||||
);
|
||||
const { reward: reward1, stake: rewardStake1 } = await rewardPoolMembersAsync({
|
||||
poolId,
|
||||
stake: new BigNumber(delegatorStake).times(2),
|
||||
});
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
// rewards paid for stake in epoch 2
|
||||
const { reward: reward2, stake: rewardStake2 } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: new BigNumber(delegatorStake).times(3) },
|
||||
);
|
||||
const delegatorReward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const { reward: reward2, stake: rewardStake2 } = await rewardPoolMembersAsync({
|
||||
poolId,
|
||||
stake: new BigNumber(delegatorStake).times(3),
|
||||
});
|
||||
const delegatorReward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedDelegatorReward = BigNumber.sum(
|
||||
computeDelegatorRewards(reward1, delegatorStake, rewardStake1),
|
||||
computeDelegatorRewards(reward2, delegatorStake, rewardStake2),
|
||||
@ -410,7 +416,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId, { stake: 0 });
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(reward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
@ -419,32 +425,32 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(reward).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
it('returns only unfinalized rewards from epoch 2 for delegator delegating in epoch 1', async () => {
|
||||
it('returns unfinalized rewards from epoch 2 for delegator delegating in epoch 0', async () => {
|
||||
const poolId = hexRandom();
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(reward).to.bignumber.eq(unfinalizedReward);
|
||||
});
|
||||
|
||||
it('returns only unfinalized rewards from epoch 3 for delegator delegating in epoch 1', async () => {
|
||||
it('returns unfinalized rewards from epoch 3 for delegator delegating in epoch 0', async () => {
|
||||
const poolId = hexRandom();
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(reward).to.bignumber.eq(unfinalizedReward);
|
||||
});
|
||||
|
||||
it('returns unfinalized rewards from epoch 3 + rewards from epoch 2 for delegator delegating in epoch 1', async () => {
|
||||
it('returns unfinalized rewards from epoch 3 + rewards from epoch 2 for delegator delegating in epoch 0', async () => {
|
||||
const poolId = hexRandom();
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
@ -452,7 +458,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { reward: prevReward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedReward = BigNumber.sum(prevReward, unfinalizedReward);
|
||||
expect(reward).to.bignumber.eq(expectedReward);
|
||||
});
|
||||
@ -466,7 +472,7 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
await advanceEpochAsync(); // epoch 4
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake });
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedReward = BigNumber.sum(prevReward, unfinalizedReward);
|
||||
expect(reward).to.bignumber.eq(expectedReward);
|
||||
});
|
||||
@ -476,16 +482,17 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { delegator, stake } = await delegateStakeAsync(poolId);
|
||||
await advanceEpochAsync(); // epoch 1
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
const { reward: prevReward, stake: prevStake } = await rewardPoolMembersAsync(
|
||||
{ poolId, stake: new BigNumber(stake).times(2) },
|
||||
);
|
||||
const { reward: prevReward, stake: prevStake } = await rewardPoolMembersAsync({
|
||||
poolId,
|
||||
stake: new BigNumber(stake).times(2),
|
||||
});
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
await advanceEpochAsync(); // epoch 4
|
||||
const { reward: unfinalizedReward, stake: unfinalizedStake } =
|
||||
await setUnfinalizedMembersRewardsAsync(
|
||||
{ poolId, stake: new BigNumber(stake).times(5) },
|
||||
);
|
||||
const reward = await getDelegatorRewardAsync(poolId, delegator);
|
||||
const { reward: unfinalizedReward, stake: unfinalizedStake } = await setUnfinalizedMembersRewardsAsync({
|
||||
poolId,
|
||||
stake: new BigNumber(stake).times(5),
|
||||
});
|
||||
const reward = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
const expectedReward = BigNumber.sum(
|
||||
computeDelegatorRewards(prevReward, stake, prevStake),
|
||||
computeDelegatorRewards(unfinalizedReward, stake, unfinalizedStake),
|
||||
@ -504,7 +511,9 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
// rewards paid for stake in epoch 1
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const { deposit } = await touchStakeAsync(poolId, delegator);
|
||||
const finalRewardBalance = await getDelegatorRewardBalanceAsync(poolId, delegator);
|
||||
expect(deposit).to.bignumber.eq(reward);
|
||||
expect(finalRewardBalance).to.bignumber.eq(0);
|
||||
});
|
||||
|
||||
it('does not collect extra rewards from delegating more stake in the reward epoch', async () => {
|
||||
@ -513,21 +522,21 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
// stake
|
||||
stakeResults.push(await delegateStakeAsync(poolId));
|
||||
const { delegator, stake } = stakeResults[0];
|
||||
const totalStake = new BigNumber(stake).times(2);
|
||||
const rewardStake = new BigNumber(stake).times(2);
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
// add more stake.
|
||||
stakeResults.push(await delegateStakeAsync(poolId, { delegator, stake }));
|
||||
await advanceEpochAsync(); // epoch 1 (2 * stake now active)
|
||||
// reward for epoch 1, using 2 * stake so delegator should
|
||||
// only be entitled to a fraction of the rewards.
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// touch the stake one last time
|
||||
stakeResults.push(await touchStakeAsync(poolId, delegator));
|
||||
// Should only see deposits for epoch 2.
|
||||
const expectedDeposit = computeDelegatorRewards(reward, stake, totalStake);
|
||||
const allDeposits = stakeResults.map(r => r.deposit);
|
||||
assertRoughlyEquals(BigNumber.sum(...allDeposits), expectedDeposit);
|
||||
const expectedReward = computeDelegatorRewards(reward, stake, rewardStake);
|
||||
assertRoughlyEquals(BigNumber.sum(...allDeposits), expectedReward);
|
||||
});
|
||||
|
||||
it('only collects rewards from staked epochs', async () => {
|
||||
@ -536,27 +545,46 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
// stake
|
||||
stakeResults.push(await delegateStakeAsync(poolId));
|
||||
const { delegator, stake } = stakeResults[0];
|
||||
await advanceEpochAsync(); // epoch 1 (stake now active)
|
||||
// unstake before and after reward payout, to be extra sneaky.
|
||||
const unstake1 = new BigNumber(stake).dividedToIntegerBy(2);
|
||||
stakeResults.push(await undelegateStakeAsync(poolId, delegator, unstake1));
|
||||
const rewardStake = new BigNumber(stake).times(2);
|
||||
await advanceEpochAsync(); // epoch 1 (full stake now active)
|
||||
// reward for epoch 0
|
||||
await rewardPoolMembersAsync({ poolId, stake });
|
||||
const unstake2 = new BigNumber(stake).minus(unstake1);
|
||||
stakeResults.push(await undelegateStakeAsync(poolId, delegator, unstake2));
|
||||
await advanceEpochAsync(); // epoch 2 (no active stake)
|
||||
await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
// unstake some
|
||||
const unstake = new BigNumber(stake).dividedToIntegerBy(2);
|
||||
stakeResults.push(await undelegateStakeAsync(poolId, delegator, unstake));
|
||||
await advanceEpochAsync(); // epoch 2 (half active stake)
|
||||
// reward for epoch 1
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake });
|
||||
const { reward: reward1 } = await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
// re-stake
|
||||
stakeResults.push(await delegateStakeAsync(poolId, { delegator, stake }));
|
||||
await advanceEpochAsync(); // epoch 3 (stake now active)
|
||||
stakeResults.push(await delegateStakeAsync(poolId, { delegator, stake: unstake }));
|
||||
await advanceEpochAsync(); // epoch 3 (full stake now active)
|
||||
// reward for epoch 2
|
||||
await rewardPoolMembersAsync({ poolId, stake });
|
||||
// touch the stake one last time
|
||||
const { reward: reward2 } = await rewardPoolMembersAsync({ poolId, stake: rewardStake });
|
||||
// touch the stake to claim rewards
|
||||
stakeResults.push(await touchStakeAsync(poolId, delegator));
|
||||
// Should only see deposits for epoch 2.
|
||||
const allDeposits = stakeResults.map(r => r.deposit);
|
||||
assertRoughlyEquals(BigNumber.sum(...allDeposits), reward);
|
||||
const expectedReward = BigNumber.sum(
|
||||
computeDelegatorRewards(reward1, stake, rewardStake),
|
||||
computeDelegatorRewards(reward2, new BigNumber(stake).minus(unstake), rewardStake),
|
||||
);
|
||||
assertRoughlyEquals(BigNumber.sum(...allDeposits), expectedReward);
|
||||
});
|
||||
|
||||
it('two delegators can collect split rewards as soon as available', async () => {
|
||||
const poolId = hexRandom();
|
||||
const { delegator: delegatorA, stake: stakeA } = await delegateStakeAsync(poolId);
|
||||
const { delegator: delegatorB, stake: stakeB } = await delegateStakeAsync(poolId);
|
||||
const totalStake = BigNumber.sum(stakeA, stakeB);
|
||||
await advanceEpochAsync(); // epoch 1 (stakes now active)
|
||||
await advanceEpochAsync(); // epoch 2
|
||||
// rewards paid for stake in epoch 1
|
||||
const { reward } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
// delegator A will finalize and collect rewards by touching stake.
|
||||
const { deposit: depositA } = await touchStakeAsync(poolId, delegatorA);
|
||||
assertRoughlyEquals(depositA, computeDelegatorRewards(reward, stakeA, totalStake));
|
||||
// delegator B will collect rewards by touching stake
|
||||
const { deposit: depositB } = await touchStakeAsync(poolId, delegatorB);
|
||||
assertRoughlyEquals(depositB, computeDelegatorRewards(reward, stakeB, totalStake));
|
||||
});
|
||||
|
||||
it('delegator B collects correct rewards after delegator A finalizes', async () => {
|
||||
@ -570,7 +598,10 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { reward: prevReward } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
// unfinalized rewards for stake in epoch 2
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake: totalStake });
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({
|
||||
poolId,
|
||||
stake: totalStake,
|
||||
});
|
||||
const totalRewards = BigNumber.sum(prevReward, unfinalizedReward);
|
||||
// delegator A will finalize and collect rewards by touching stake.
|
||||
const { deposit: depositA } = await touchStakeAsync(poolId, delegatorA);
|
||||
@ -591,7 +622,10 @@ blockchainTests.resets.only('delegator unit rewards', env => {
|
||||
const { reward: prevReward } = await rewardPoolMembersAsync({ poolId, stake: totalStake });
|
||||
await advanceEpochAsync(); // epoch 3
|
||||
// unfinalized rewards for stake in epoch 2
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({ poolId, stake: totalStake });
|
||||
const { reward: unfinalizedReward } = await setUnfinalizedMembersRewardsAsync({
|
||||
poolId,
|
||||
stake: totalStake,
|
||||
});
|
||||
const totalRewards = BigNumber.sum(prevReward, unfinalizedReward);
|
||||
// finalize
|
||||
await finalizePoolAsync(poolId);
|
||||
|
@ -255,7 +255,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
// Add a pool so there is state to clear.
|
||||
await addActivePoolAsync();
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
assertFinalizationStateAsync({
|
||||
return assertFinalizationStateAsync({
|
||||
currentEpoch: INITIAL_EPOCH + 1,
|
||||
closingEpoch: INITIAL_EPOCH,
|
||||
numActivePoolsThisEpoch: 0,
|
||||
@ -268,7 +268,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
// Add a pool so there is state to clear.
|
||||
const pool = await addActivePoolAsync();
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
assertFinalizationStateAsync({
|
||||
return assertFinalizationStateAsync({
|
||||
unfinalizedPoolsRemaining: 1,
|
||||
unfinalizedRewardsAvailable: INITIAL_BALANCE,
|
||||
unfinalizedTotalFeesCollected: pool.feesCollected,
|
||||
@ -318,7 +318,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
|
||||
it('can finalize multiple pools', async () => {
|
||||
const nextEpoch = INITIAL_EPOCH + 1;
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const poolIds = pools.map(p => p.poolId);
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
||||
@ -338,7 +338,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
|
||||
it('can finalize multiple pools over multiple transactions', async () => {
|
||||
const nextEpoch = INITIAL_EPOCH + 1;
|
||||
const pools = await Promise.all(_.times(2, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const receipts = await Promise.all(
|
||||
pools.map(pool => testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId])),
|
||||
@ -358,7 +358,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('ignores a non-active pool', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const nonActivePoolId = hexRandom();
|
||||
const poolIds = _.shuffle([...pools.map(p => p.poolId), nonActivePoolId]);
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
@ -371,7 +371,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('ignores a finalized pool', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const poolIds = pools.map(p => p.poolId);
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const [finalizedPool] = _.sampleSize(pools, 1);
|
||||
@ -385,7 +385,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('resets pool state after finalizing it', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const pool = _.sample(pools) as ActivePoolOpts;
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
||||
@ -399,7 +399,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('`rewardsPaid` is the sum of all pool rewards', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const poolIds = pools.map(p => p.poolId);
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
||||
@ -412,7 +412,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('`rewardsPaid` <= `rewardsAvailable` <= contract balance at the end of the epoch', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const poolIds = pools.map(p => p.poolId);
|
||||
let receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||
@ -438,7 +438,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
for (const i of _.times(numTests)) {
|
||||
const numPools = _.random(1, 32);
|
||||
it(`${i + 1}/${numTests} \`rewardsPaid\` <= \`rewardsAvailable\` (${numPools} pools)`, async () => {
|
||||
const pools = await Promise.all(_.times(numPools, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(numPools, async () => addActivePoolAsync()));
|
||||
const poolIds = pools.map(p => p.poolId);
|
||||
let receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||
@ -477,7 +477,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
|
||||
it('can finalize multiple pools over multiple transactions', async () => {
|
||||
const nextEpoch = INITIAL_EPOCH + 1;
|
||||
const pools = await Promise.all(_.times(2, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(2, async () => addActivePoolAsync()));
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const receipts = await Promise.all(
|
||||
pools.map(pool => testContract.internalFinalizePool.awaitTransactionSuccessAsync(pool.poolId)),
|
||||
@ -497,7 +497,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('ignores a finalized pool', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const [finalizedPool] = _.sampleSize(pools, 1);
|
||||
await testContract.internalFinalizePool.awaitTransactionSuccessAsync(finalizedPool.poolId);
|
||||
@ -507,7 +507,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('resets pool state after finalizing it', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const pool = _.sample(pools) as ActivePoolOpts;
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
await testContract.internalFinalizePool.awaitTransactionSuccessAsync(pool.poolId);
|
||||
@ -521,7 +521,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('`rewardsPaid` <= `rewardsAvailable` <= contract balance at the end of the epoch', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||
expect(rewardsAvailable).to.bignumber.lte(INITIAL_BALANCE);
|
||||
@ -551,7 +551,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
for (const i of _.times(numTests)) {
|
||||
const numPools = _.random(1, 32);
|
||||
it(`${i + 1}/${numTests} \`rewardsPaid\` <= \`rewardsAvailable\` (${numPools} pools)`, async () => {
|
||||
const pools = await Promise.all(_.times(numPools, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(numPools, async () => addActivePoolAsync()));
|
||||
const receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||
const receipts = await Promise.all(
|
||||
@ -601,11 +601,11 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
|
||||
it('rolls over leftover rewards into th next epoch', async () => {
|
||||
const poolIds = _.times(3, () => hexRandom());
|
||||
await Promise.all(poolIds.map(id => addActivePoolAsync({ poolId: id })));
|
||||
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
let receipt = await testContract.finalizePools.awaitTransactionSuccessAsync(poolIds);
|
||||
const { rewardsRemaining: rolledOverRewards } = getEpochFinalizedEvents(receipt.logs)[0];
|
||||
await Promise.all(poolIds.map(id => addActivePoolAsync({ poolId: id })));
|
||||
await Promise.all(poolIds.map(async id => addActivePoolAsync({ poolId: id })));
|
||||
receipt = await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const { rewardsAvailable } = getEpochEndedEvents(receipt.logs)[0];
|
||||
expect(rewardsAvailable).to.bignumber.eq(rolledOverRewards);
|
||||
@ -690,7 +690,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('returns empty if pool was already finalized', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
const [pool] = _.sampleSize(pools, 1);
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
await testContract.finalizePools.awaitTransactionSuccessAsync([pool.poolId]);
|
||||
@ -712,7 +712,7 @@ blockchainTests.resets('finalizer unit tests', env => {
|
||||
});
|
||||
|
||||
it('computes one reward among multiple pools', async () => {
|
||||
const pools = await Promise.all(_.times(3, () => addActivePoolAsync()));
|
||||
const pools = await Promise.all(_.times(3, async () => addActivePoolAsync()));
|
||||
await testContract.endEpoch.awaitTransactionSuccessAsync();
|
||||
const [pool] = _.sampleSize(pools, 1);
|
||||
const totalFeesCollected = BigNumber.sum(...pools.map(p => p.feesCollected));
|
||||
|
@ -1,12 +1,4 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
hexConcat,
|
||||
hexRandom,
|
||||
hexSlice,
|
||||
testCombinatoriallyWithReferenceFunc,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { blockchainTests, constants, expect, hexConcat, hexRandom, hexSlice } from '@0x/contracts-test-utils';
|
||||
import { StakingRevertErrors } from '@0x/order-utils';
|
||||
import { cartesianProduct } from 'js-combinatorics';
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user