From 7c2c607c288fd7ad43c7ec1a7cb57487ca08b036 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Tue, 16 Apr 2024 14:48:05 +0300 Subject: [PATCH 1/9] updates create new _calculatePositionRewards method that will be used for the rewards calculation when getting the rewards; update _rewardParams to calculate elapsed time, not only the maturing end period; make a test case that tests if the same vesting position size, but with different period shows different rewards and also if they are totally the same, opened in the same time and they've the same rewards. --- contracts/RewardPool/IRewardPool.sol | 7 +- .../RewardPool/modules/DelegationRewards.sol | 99 ++++++----- docs/RewardPool/IRewardPool.md | 4 +- docs/RewardPool/RewardPool.md | 4 +- docs/RewardPool/RewardPoolBase.md | 4 +- docs/RewardPool/modules/DelegationRewards.md | 4 +- docs/RewardPool/modules/StakingRewards.md | 4 +- test/RewardPool/RewardPool.test.ts | 155 +++++++++++++++++- test/ValidatorSet/Delegation.test.ts | 50 +++--- test/constants.ts | 1 + test/helper.ts | 46 +++++- 11 files changed, 298 insertions(+), 80 deletions(-) diff --git a/contracts/RewardPool/IRewardPool.sol b/contracts/RewardPool/IRewardPool.sol index 79c78833..b48b1580 100644 --- a/contracts/RewardPool/IRewardPool.sol +++ b/contracts/RewardPool/IRewardPool.sol @@ -210,13 +210,18 @@ interface IRewardPool { * @param validator The address of the validator * @param delegator The address of the delegator * @param amount The amount that is going to be undelegated + * @param epochNumber Epoch where the last claimable reward is distributed + * We need it because not all rewards are matured at the moment of calling the function + * @param topUpIndex The index when a topup has been made * @return penalty for the delegator * @return reward of the delegator */ function calculateDelegatePositionPenalty( address validator, address delegator, - uint256 amount + uint256 amount, + uint256 epochNumber, + uint256 topUpIndex ) external view returns (uint256 penalty, uint256 reward); /** diff --git a/contracts/RewardPool/modules/DelegationRewards.sol b/contracts/RewardPool/modules/DelegationRewards.sol index 3729f7e0..5802ffa9 100644 --- a/contracts/RewardPool/modules/DelegationRewards.sol +++ b/contracts/RewardPool/modules/DelegationRewards.sol @@ -76,43 +76,8 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa uint256 topUpIndex ) external view returns (uint256) { VestingPosition memory position = delegationPositions[validator][delegator]; - if (_noRewardConditions(position)) { - return 0; - } - - uint256 sumReward; - DelegationPool storage delegationPool = delegationPools[validator]; - bool rsi = true; - if (_isTopUpMade(validator, delegator)) { - rsi = false; - RewardParams memory params = beforeTopUpParams[validator][delegator]; - uint256 rsiReward = delegationPool.claimableRewards( - delegator, - params.rewardPerShare, - params.balance, - params.correction - ); - sumReward += _applyCustomReward(position, rsiReward, true); - } - (uint256 epochRPS, uint256 balance, int256 correction) = _rewardParams( - validator, - delegator, - epochNumber, - topUpIndex - ); - uint256 reward = delegationPool.claimableRewards(delegator, epochRPS, balance, correction) - sumReward; - reward = _applyCustomReward(position, reward, rsi); - sumReward += reward; - - // If the full maturing period is finished, withdraw also the reward made after the vesting period - if (block.timestamp > position.end + position.duration) { - uint256 additionalReward = delegationPool.claimableRewards(delegator) - sumReward; - additionalReward = _applyCustomReward(additionalReward); - sumReward += additionalReward; - } - - return sumReward; + return _calculatePositionRewards(validator, delegator, position, epochNumber, topUpIndex); } /** @@ -121,14 +86,14 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa function calculateDelegatePositionPenalty( address validator, address delegator, - uint256 amount + uint256 amount, + uint256 epochNumber, + uint256 topUpIndex ) external view returns (uint256 penalty, uint256 reward) { - DelegationPool storage pool = delegationPools[validator]; VestingPosition memory position = delegationPositions[validator][delegator]; if (position.isActive()) { penalty = _calcSlashing(position, amount); - // apply the max Vesting bonus, because the full reward must be burned - reward = applyMaxReward(pool.claimableRewards(delegator)); + reward = _calculatePositionRewards(validator, delegator, position, epochNumber, topUpIndex); } } @@ -422,14 +387,17 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa ) private view returns (uint256 rps, uint256 balance, int256 correction) { VestingPosition memory position = delegationPositions[validator][manager]; uint256 matureEnd = position.end + position.duration; - uint256 alreadyMatured; + uint256 timeElapsed; // If full mature period is finished, the full reward up to the end of the vesting must be matured if (matureEnd < block.timestamp) { - alreadyMatured = position.end; - } else { + timeElapsed = position.end; + } else if (position.end < block.timestamp) { // rewardPerShare must be fetched from the history records uint256 maturedPeriod = block.timestamp - position.end; - alreadyMatured = position.start + maturedPeriod; + timeElapsed = position.start + maturedPeriod; + } else { + uint256 passedPeriod = block.timestamp - position.start; + timeElapsed = position.start + passedPeriod; } RPS memory rpsData = historyRPS[validator][epochNumber]; @@ -438,7 +406,7 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa } // If the given RPS is for future time - it is wrong, so revert - if (rpsData.timestamp > alreadyMatured) { + if (rpsData.timestamp > timeElapsed) { revert DelegateRequirement({src: "vesting", msg: "WRONG_RPS"}); } @@ -580,6 +548,47 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa return (params.balance, params.correction); } + function _calculatePositionRewards( + address validator, + address delegator, + VestingPosition memory position, + uint256 epochNumber, + uint256 topUpIndex + ) private view returns (uint256 sumReward) { + DelegationPool storage delegationPool = delegationPools[validator]; + bool rsi = true; + if (_isTopUpMade(validator, delegator)) { + rsi = false; + RewardParams memory params = beforeTopUpParams[validator][delegator]; + uint256 rsiReward = delegationPool.claimableRewards( + delegator, + params.rewardPerShare, + params.balance, + params.correction + ); + sumReward += _applyCustomReward(position, rsiReward, true); + } + + // distribute the proper vesting reward + (uint256 epochRPS, uint256 balance, int256 correction) = _rewardParams( + validator, + delegator, + epochNumber, + topUpIndex + ); + + uint256 reward = delegationPool.claimableRewards(delegator, epochRPS, balance, correction); + reward = _applyCustomReward(position, reward, rsi); + sumReward += reward; + + // If the full maturing period is finished, withdraw also the reward made after the vesting period + if (block.timestamp > position.end + position.duration) { + uint256 additionalReward = delegationPool.claimableRewards(delegator) - sumReward; + additionalReward = _applyCustomReward(additionalReward); + sumReward += additionalReward; + } + } + function _burnAmount(uint256 amount) private { (bool success, ) = address(0).call{value: amount}(""); require(success, "Failed to burn amount"); diff --git a/docs/RewardPool/IRewardPool.md b/docs/RewardPool/IRewardPool.md index 79ebe737..a82be399 100644 --- a/docs/RewardPool/IRewardPool.md +++ b/docs/RewardPool/IRewardPool.md @@ -13,7 +13,7 @@ ### calculateDelegatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty, uint256 reward) +function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) ``` Returns the penalty and reward that will be burned, if vested delegate position is active @@ -27,6 +27,8 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | +| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | +| topUpIndex | uint256 | The index when a topup has been made | #### Returns diff --git a/docs/RewardPool/RewardPool.md b/docs/RewardPool/RewardPool.md index 403fff74..ae1678b4 100644 --- a/docs/RewardPool/RewardPool.md +++ b/docs/RewardPool/RewardPool.md @@ -298,7 +298,7 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa ### calculateDelegatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty, uint256 reward) +function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) ``` Returns the penalty and reward that will be burned, if vested delegate position is active @@ -312,6 +312,8 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | +| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | +| topUpIndex | uint256 | The index when a topup has been made | #### Returns diff --git a/docs/RewardPool/RewardPoolBase.md b/docs/RewardPool/RewardPoolBase.md index 59d59624..a31b86c0 100644 --- a/docs/RewardPool/RewardPoolBase.md +++ b/docs/RewardPool/RewardPoolBase.md @@ -13,7 +13,7 @@ the base state variables and functionality needed in different modules that the ### calculateDelegatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty, uint256 reward) +function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) ``` Returns the penalty and reward that will be burned, if vested delegate position is active @@ -27,6 +27,8 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | +| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | +| topUpIndex | uint256 | The index when a topup has been made | #### Returns diff --git a/docs/RewardPool/modules/DelegationRewards.md b/docs/RewardPool/modules/DelegationRewards.md index 73263ee8..ff204b72 100644 --- a/docs/RewardPool/modules/DelegationRewards.md +++ b/docs/RewardPool/modules/DelegationRewards.md @@ -196,7 +196,7 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa ### calculateDelegatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty, uint256 reward) +function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) ``` Returns the penalty and reward that will be burned, if vested delegate position is active @@ -210,6 +210,8 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | +| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | +| topUpIndex | uint256 | The index when a topup has been made | #### Returns diff --git a/docs/RewardPool/modules/StakingRewards.md b/docs/RewardPool/modules/StakingRewards.md index fc2bd40e..1305090d 100644 --- a/docs/RewardPool/modules/StakingRewards.md +++ b/docs/RewardPool/modules/StakingRewards.md @@ -196,7 +196,7 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa ### calculateDelegatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty, uint256 reward) +function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) ``` Returns the penalty and reward that will be burned, if vested delegate position is active @@ -210,6 +210,8 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | +| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | +| topUpIndex | uint256 | The index when a topup has been made | #### Returns diff --git a/test/RewardPool/RewardPool.test.ts b/test/RewardPool/RewardPool.test.ts index 550a4fc4..5315be1f 100644 --- a/test/RewardPool/RewardPool.test.ts +++ b/test/RewardPool/RewardPool.test.ts @@ -3,12 +3,14 @@ import { loadFixture, time } from "@nomicfoundation/hardhat-network-helpers"; import * as hre from "hardhat"; import { expect } from "chai"; -import { EPOCHS_YEAR, WEEK } from "../constants"; +import { EPOCHS_YEAR, VESTING_DURATION_WEEKS, WEEK } from "../constants"; import { calculateExpectedReward, commitEpoch, commitEpochs, + createManagerAndVest, findProperRPSIndex, + getDelegatorPositionReward, getValidatorReward, retrieveRPSData, } from "../helper"; @@ -162,6 +164,157 @@ export function RunDelegateClaimTests(): void { }); } +export function RunVestedDelegationRewardsTests(): void { + describe("getDelegatorPositionReward", async function () { + it("should have the same rewards if the position size and period are the same", async function () { + const { systemValidatorSet, validatorSet, rewardPool } = await loadFixture(this.fixtures.delegatedFixture); + + const validator = this.signers.validators[2]; + const manager1 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(100) + ); + const manager2 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(100) + ); + + // pass two weeks ahead + await time.increase(WEEK * 2); + + // Commit epochs so rewards to be distributed + await commitEpochs( + systemValidatorSet, + rewardPool, + [this.signers.validators[0], this.signers.validators[1], validator], + 10, // number of epochs to commit + this.epochSize + ); + + const manager1rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager1.address + ); + const manager2rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager2.address + ); + + expect(manager1rewards).to.equal(manager2rewards); + }); + + it("should have different rewards if the position period differs", async function () { + const { systemValidatorSet, validatorSet, rewardPool } = await loadFixture(this.fixtures.delegatedFixture); + + const validator = this.signers.validators[2]; + const manager1 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(100) + ); + const manager2 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + 52, // max weeks + this.minDelegation.mul(100) + ); + + // pass two weeks ahead + await time.increase(WEEK * 3); + + // Commit epochs so rewards to be distributed + await commitEpochs( + systemValidatorSet, + rewardPool, + [this.signers.validators[0], this.signers.validators[1], validator], + 20, // number of epochs to commit + this.epochSize + ); + + const manager1rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager1.address + ); + const manager2rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager2.address + ); + + expect(manager2rewards).to.be.greaterThan(manager1rewards); + }); + + it("should have different rewards when the position differs", async function () { + const { systemValidatorSet, validatorSet, rewardPool } = await loadFixture(this.fixtures.delegatedFixture); + + const validator = this.signers.validators[2]; + const manager1 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(100) + ); + const manager2 = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(2) + ); + + // pass two weeks ahead + await time.increase(WEEK * 5); + + // Commit epochs so rewards to be distributed + await commitEpochs( + systemValidatorSet, + rewardPool, + [this.signers.validators[0], this.signers.validators[1], validator], + 20, // number of epochs to commit + this.epochSize + ); + + const manager1rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager1.address + ); + const manager2rewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager2.address + ); + + expect(manager1rewards).to.be.greaterThan(manager2rewards); + }); + }); +} + export function RunVestedDelegateClaimTests(): void { describe("Claim delegation rewards", async function () { it("should revert when not the vest manager owner", async function () { diff --git a/test/ValidatorSet/Delegation.test.ts b/test/ValidatorSet/Delegation.test.ts index 3c538635..012c6a3d 100644 --- a/test/ValidatorSet/Delegation.test.ts +++ b/test/ValidatorSet/Delegation.test.ts @@ -6,8 +6,19 @@ import * as hre from "hardhat"; // eslint-disable-next-line camelcase import { VestManager__factory } from "../../typechain-types"; import { VESTING_DURATION_WEEKS, WEEK } from "../constants"; -import { calculatePenalty, claimPositionRewards, commitEpoch, commitEpochs, getUserManager } from "../helper"; -import { RunDelegateClaimTests, RunVestedDelegateClaimTests } from "../RewardPool/RewardPool.test"; +import { + calculatePenalty, + claimPositionRewards, + commitEpoch, + commitEpochs, + getUserManager, + retrieveRPSData, +} from "../helper"; +import { + RunDelegateClaimTests, + RunVestedDelegateClaimTests, + RunVestedDelegationRewardsTests, +} from "../RewardPool/RewardPool.test"; export function RunDelegationTests(): void { describe("Delegate", function () { @@ -451,11 +462,21 @@ export function RunDelegationTests(): void { const position = await rewardPool.delegationPositions(delegatedValidator.address, vestManager.address); const latestTimestamp = hre.ethers.BigNumber.from(await time.latest()); + // prepare params for call + const { epochNum, topUpIndex } = await retrieveRPSData( + systemValidatorSet, + rewardPool, + delegatedValidator.address, + vestManager.address + ); + // get the penalty and reward from the contract const { penalty, reward } = await rewardPool.calculateDelegatePositionPenalty( delegatedValidator.address, vestManager.address, - this.minStake + this.minStake, + epochNum, + topUpIndex ); // calculate penalty locally @@ -480,25 +501,6 @@ export function RunDelegationTests(): void { const cutAmount = delegatedBalanceBefore.div(2); const position = await rewardPool.delegationPositions(delegatedValidator.address, vestManager.address); - // Hydra TODO: Create table-driven unit tests with precalculated values to test the exact amounts - // check if amount is properly burned - // const end = position.end; - // const rpsValues = await childValidatorSet.getRPSValues(validator); - // const epochNum = findProperRPSIndex(rpsValues, end); - // const topUpIndex = 0; - // let reward = await childValidatorSet.getDelegatorPositionReward( - // validator, - // manager.address, - // epochNum, - // topUpIndex - // ); - // reward = await childValidatorSet.applyMaxReward(reward); - // const decrease = reward.add(amountToBeBurned); - // await expect(manager.cutVestedDelegatePosition(validator, cutAmount)).to.changeEtherBalance( - // childValidatorSet, - // decrease.mul(-1) - // ); - await liquidToken.connect(vestManagerOwner).approve(vestManager.address, cutAmount); const latestTimestamp = hre.ethers.BigNumber.from(await time.latest()); @@ -825,6 +827,10 @@ export function RunDelegationTests(): void { }); }); + describe("Reward Pool - rewards", async function () { + RunVestedDelegationRewardsTests(); + }); + describe("Reward Pool - Vested delegate claim", async function () { RunVestedDelegateClaimTests(); }); diff --git a/test/constants.ts b/test/constants.ts index 515933b5..92719364 100644 --- a/test/constants.ts +++ b/test/constants.ts @@ -13,6 +13,7 @@ export const MAX_COMMISSION = ethers.BigNumber.from(100); export const WEEK = 60 * 60 * 24 * 7; export const VESTING_DURATION_WEEKS = 10; // in weeks export const EPOCHS_YEAR = 31500; +export const DENOMINATOR = 10000; /// @notice This bytecode is used to mock and return true with any input export const alwaysTrueBytecode = "0x600160005260206000F3"; diff --git a/test/helper.ts b/test/helper.ts index 39f88eb5..76425ff3 100644 --- a/test/helper.ts +++ b/test/helper.ts @@ -10,7 +10,7 @@ import { ValidatorSet } from "../typechain-types/contracts/ValidatorSet"; import { RewardPool } from "../typechain-types/contracts/RewardPool"; import { VestManager } from "../typechain-types/contracts/ValidatorSet/modules/Delegation"; import { VestManager__factory } from "../typechain-types/factories/contracts/ValidatorSet/modules/Delegation"; -import { CHAIN_ID, DOMAIN, EPOCHS_YEAR, INITIAL_COMMISSION, SYSTEM, WEEK } from "./constants"; +import { CHAIN_ID, DENOMINATOR, DOMAIN, EPOCHS_YEAR, INITIAL_COMMISSION, SYSTEM, WEEK } from "./constants"; interface RewardParams { timestamp: BigNumber; @@ -181,7 +181,7 @@ export async function calculatePenalty(position: any, timestamp: BigNumber, amou // basis points used for precise percentage calculations const bps = leftWeeks.mul(30); - return amount.mul(bps).div(10000); + return amount.mul(bps).div(DENOMINATOR); } export async function getUserManager( @@ -253,7 +253,7 @@ export async function calculateExpectedReward( .add(vestBonus) .mul(rsi) .mul(reward) - .div(10000 * 10000) + .div(DENOMINATOR * DENOMINATOR) .div(EPOCHS_YEAR); } @@ -267,7 +267,7 @@ export async function applyMaxReward(rewardPool: RewardPool, reward: BigNumber) .add(vestBonus) .mul(rsi) .mul(reward) - .div(10000 * 10000) + .div(DENOMINATOR * DENOMINATOR) .div(EPOCHS_YEAR); } @@ -281,10 +281,10 @@ export async function applyCustomReward( const position = await rewardPool.delegationPositions(validator, delegator); let bonus = position.base.add(position.vestBonus); - let divider = 10000; + let divider = DENOMINATOR; if (rsi) { bonus = bonus.mul(position.rsiBonus); - divider *= 10000; + divider *= DENOMINATOR; } return reward.mul(bonus).div(divider).div(EPOCHS_YEAR); @@ -310,3 +310,37 @@ export function generateValidatorBls(account: SignerWithAddress) { export function genValSignature(account: SignerWithAddress, keyPair: mcl.keyPair) { return mcl.signValidatorMessage(DOMAIN, CHAIN_ID, account.address, keyPair.secret).signature; } + +export async function createManagerAndVest( + validatorSet: ValidatorSet, + rewardPool: RewardPool, + account: SignerWithAddress, + validator: string, + duration: number, + amount: BigNumber +) { + const { newManager } = await createNewVestManager(validatorSet, rewardPool, account); + + await newManager.openVestedDelegatePosition(validator, duration, { + value: amount, + }); + + return newManager; +} + +export async function getDelegatorPositionReward( + validatorSet: ValidatorSet, + rewardPool: RewardPool, + validator: string, + delegator: string +) { + // prepare params for call + const { epochNum: epochNumManager1, topUpIndex: topUpIndexNumManager1 } = await retrieveRPSData( + validatorSet, + rewardPool, + validator, + delegator + ); + + return await rewardPool.getDelegatorPositionReward(validator, delegator, epochNumManager1, topUpIndexNumManager1); +} From 1dc9aaf8a85b3ecde0420f3f394252faea4887c0 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Wed, 17 Apr 2024 14:24:04 +0300 Subject: [PATCH 2/9] update and polish --- contracts/RewardPool/IRewardPool.sol | 30 ++-- contracts/RewardPool/modules/APR.sol | 4 + .../RewardPool/modules/DelegationRewards.sol | 134 +++++++++--------- contracts/RewardPool/modules/Vesting.sol | 4 + docs/RewardPool/IRewardPool.md | 55 ++++--- docs/RewardPool/RewardPool.md | 38 +++-- docs/RewardPool/RewardPoolBase.md | 55 ++++--- docs/RewardPool/modules/DelegationRewards.md | 38 +++-- docs/RewardPool/modules/StakingRewards.md | 55 ++++--- test/RewardPool/RewardPool.test.ts | 109 +++++++++----- test/ValidatorSet/Delegation.test.ts | 24 +--- test/helper.ts | 11 +- 12 files changed, 304 insertions(+), 253 deletions(-) diff --git a/contracts/RewardPool/IRewardPool.sol b/contracts/RewardPool/IRewardPool.sol index b48b1580..3d476a2b 100644 --- a/contracts/RewardPool/IRewardPool.sol +++ b/contracts/RewardPool/IRewardPool.sol @@ -150,14 +150,6 @@ interface IRewardPool { */ function getValidatorReward(address validator) external view returns (uint256); - /** - * @notice Gets delegators's unclaimed rewards without custom rewards - * @param validator Address of validator - * @param delegator Address of delegator - * @return Delegator's unclaimed rewards with validator (in MATIC wei) - */ - function getRawDelegatorReward(address validator, address delegator) external view returns (uint256); - /** * @notice Gets delegators's unclaimed rewards including custom rewards * @param validator Address of validator @@ -206,23 +198,25 @@ interface IRewardPool { ) external view returns (uint256 penalty, uint256 reward); /** - * @notice Returns the penalty and reward that will be burned, if vested delegate position is active + * @notice Returns the penalty that will taken from the delegator, if the position is still active * @param validator The address of the validator * @param delegator The address of the delegator * @param amount The amount that is going to be undelegated - * @param epochNumber Epoch where the last claimable reward is distributed - * We need it because not all rewards are matured at the moment of calling the function - * @param topUpIndex The index when a topup has been made * @return penalty for the delegator - * @return reward of the delegator */ - function calculateDelegatePositionPenalty( + function calculatePositionPenalty( address validator, address delegator, - uint256 amount, - uint256 epochNumber, - uint256 topUpIndex - ) external view returns (uint256 penalty, uint256 reward); + uint256 amount + ) external view returns (uint256 penalty); + + /** + * @notice Returns the total reward that is generate for a position + * @param validator The address of the validator + * @param delegator The address of the delegator + * @return reward for the delegator + */ + function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward); /** * @notice Claims reward for the vest manager (delegator). diff --git a/contracts/RewardPool/modules/APR.sol b/contracts/RewardPool/modules/APR.sol index 38e8306e..fc9d3d3d 100644 --- a/contracts/RewardPool/modules/APR.sol +++ b/contracts/RewardPool/modules/APR.sol @@ -96,6 +96,10 @@ contract APR is Initializable, AccessControl { return 1e18; } + /// @notice Function that applies the base APR to a given reward + /// @dev Denominator is used because we should work with numbers with floating point + /// @param reward The reward to which we gonna apply the base APR + /// @return The reward with the applied APR function _applyCustomReward(uint256 reward) internal view returns (uint256) { return _applyBaseAPR(reward) / EPOCHS_YEAR; } diff --git a/contracts/RewardPool/modules/DelegationRewards.sol b/contracts/RewardPool/modules/DelegationRewards.sol index 5802ffa9..00760ee7 100644 --- a/contracts/RewardPool/modules/DelegationRewards.sol +++ b/contracts/RewardPool/modules/DelegationRewards.sol @@ -49,14 +49,6 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa return delegationPools[validator].supply; } - /** - * @inheritdoc IRewardPool - */ - function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) { - DelegationPool storage delegation = delegationPools[validator]; - return delegation.claimableRewards(delegator); - } - /** * @inheritdoc IRewardPool */ @@ -66,6 +58,8 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa return _applyCustomReward(reward); } + // vito + /** * @inheritdoc IRewardPool */ @@ -74,29 +68,69 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa address delegator, uint256 epochNumber, uint256 topUpIndex - ) external view returns (uint256) { + ) external view returns (uint256 sumReward) { VestingPosition memory position = delegationPositions[validator][delegator]; + if (_noRewardConditions(position)) { + return 0; + } + + DelegationPool storage delegationPool = delegationPools[validator]; + bool rsi = true; + if (_isTopUpMade(validator, delegator)) { + rsi = false; + RewardParams memory params = beforeTopUpParams[validator][delegator]; + uint256 rsiReward = delegationPool.claimableRewards( + delegator, + params.rewardPerShare, + params.balance, + params.correction + ); + sumReward += _applyCustomReward(position, rsiReward, true); + } - return _calculatePositionRewards(validator, delegator, position, epochNumber, topUpIndex); + // distribute the proper vesting reward + (uint256 epochRPS, uint256 balance, int256 correction) = _rewardParams( + validator, + delegator, + epochNumber, + topUpIndex + ); + + uint256 reward = delegationPool.claimableRewards(delegator, epochRPS, balance, correction); + reward = _applyCustomReward(position, reward, rsi); + sumReward += reward; + + // If the full maturing period is finished, withdraw also the reward made after the vesting period + if (block.timestamp > position.end + position.duration) { + uint256 additionalReward = delegationPool.claimableRewards(delegator) - sumReward; + additionalReward = _applyCustomReward(additionalReward); + sumReward += additionalReward; + } } /** * @inheritdoc IRewardPool */ - function calculateDelegatePositionPenalty( + function calculatePositionPenalty( address validator, address delegator, - uint256 amount, - uint256 epochNumber, - uint256 topUpIndex - ) external view returns (uint256 penalty, uint256 reward) { + uint256 amount + ) external view returns (uint256 penalty) { VestingPosition memory position = delegationPositions[validator][delegator]; if (position.isActive()) { penalty = _calcSlashing(position, amount); - reward = _calculatePositionRewards(validator, delegator, position, epochNumber, topUpIndex); } } + // vito + /** + * @inheritdoc IRewardPool + */ + function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) { + VestingPosition memory position = delegationPositions[validator][delegator]; + reward = _applyCustomReward(position, getRawDelegatorReward(validator, delegator), true); + } + /** * @inheritdoc IRewardPool */ @@ -114,6 +148,9 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa delegationPools[validator].validator = validator; } + /** + * @inheritdoc IRewardPool + */ function onDelegate(address validator, address delegator, uint256 amount) external onlyValidatorSet { DelegationPool storage delegation = delegationPools[validator]; @@ -336,6 +373,17 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa // _______________ Public functions _______________ + /** + * @notice Gets delegator's unclaimed rewards without custom rewards + * @param validator Address of validator + * @param delegator Address of delegator + * @return Delegator's unclaimed rewards with validator (in MATIC wei) + */ + function getRawDelegatorReward(address validator, address delegator) public view returns (uint256) { + DelegationPool storage delegation = delegationPools[validator]; + return delegation.claimableRewards(delegator); + } + // TODO: Check if the commitEpoch is the last transaction in the epoch, otherwise bug may occur /** * @notice Checks if balance change was already made in the current epoch @@ -387,17 +435,14 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa ) private view returns (uint256 rps, uint256 balance, int256 correction) { VestingPosition memory position = delegationPositions[validator][manager]; uint256 matureEnd = position.end + position.duration; - uint256 timeElapsed; + uint256 alreadyMatured; // If full mature period is finished, the full reward up to the end of the vesting must be matured if (matureEnd < block.timestamp) { - timeElapsed = position.end; - } else if (position.end < block.timestamp) { + alreadyMatured = position.end; + } else { // rewardPerShare must be fetched from the history records uint256 maturedPeriod = block.timestamp - position.end; - timeElapsed = position.start + maturedPeriod; - } else { - uint256 passedPeriod = block.timestamp - position.start; - timeElapsed = position.start + passedPeriod; + alreadyMatured = position.start + maturedPeriod; } RPS memory rpsData = historyRPS[validator][epochNumber]; @@ -406,7 +451,7 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa } // If the given RPS is for future time - it is wrong, so revert - if (rpsData.timestamp > timeElapsed) { + if (rpsData.timestamp > alreadyMatured) { revert DelegateRequirement({src: "vesting", msg: "WRONG_RPS"}); } @@ -548,47 +593,6 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa return (params.balance, params.correction); } - function _calculatePositionRewards( - address validator, - address delegator, - VestingPosition memory position, - uint256 epochNumber, - uint256 topUpIndex - ) private view returns (uint256 sumReward) { - DelegationPool storage delegationPool = delegationPools[validator]; - bool rsi = true; - if (_isTopUpMade(validator, delegator)) { - rsi = false; - RewardParams memory params = beforeTopUpParams[validator][delegator]; - uint256 rsiReward = delegationPool.claimableRewards( - delegator, - params.rewardPerShare, - params.balance, - params.correction - ); - sumReward += _applyCustomReward(position, rsiReward, true); - } - - // distribute the proper vesting reward - (uint256 epochRPS, uint256 balance, int256 correction) = _rewardParams( - validator, - delegator, - epochNumber, - topUpIndex - ); - - uint256 reward = delegationPool.claimableRewards(delegator, epochRPS, balance, correction); - reward = _applyCustomReward(position, reward, rsi); - sumReward += reward; - - // If the full maturing period is finished, withdraw also the reward made after the vesting period - if (block.timestamp > position.end + position.duration) { - uint256 additionalReward = delegationPool.claimableRewards(delegator) - sumReward; - additionalReward = _applyCustomReward(additionalReward); - sumReward += additionalReward; - } - } - function _burnAmount(uint256 amount) private { (bool success, ) = address(0).call{value: amount}(""); require(success, "Failed to burn amount"); diff --git a/contracts/RewardPool/modules/Vesting.sol b/contracts/RewardPool/modules/Vesting.sol index 02b8657b..a1e4071c 100644 --- a/contracts/RewardPool/modules/Vesting.sol +++ b/contracts/RewardPool/modules/Vesting.sol @@ -140,6 +140,10 @@ abstract contract Vesting is APR { historyRPS[validator][epochNumber] = RPS({value: uint192(rewardPerShare), timestamp: uint64(block.timestamp)}); } + /// @notice Function that applies the custom factors - base APR, vest bonus and rsi bonus + /// @dev Denominator is used because we should work with numbers with floating point + /// @param reward The reward to which we gonna apply the custom APR + /// @return The reward with the applied APR function _applyCustomReward( VestingPosition memory position, uint256 reward, diff --git a/docs/RewardPool/IRewardPool.md b/docs/RewardPool/IRewardPool.md index a82be399..45a2a71a 100644 --- a/docs/RewardPool/IRewardPool.md +++ b/docs/RewardPool/IRewardPool.md @@ -10,13 +10,13 @@ ## Methods -### calculateDelegatePositionPenalty +### calculatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) +function calculatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty) ``` -Returns the penalty and reward that will be burned, if vested delegate position is active +Returns the penalty that will taken from the delegator, if the position is still active @@ -27,15 +27,12 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | -| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | -| topUpIndex | uint256 | The index when a topup has been made | #### Returns | Name | Type | Description | |---|---|---| | penalty | uint256 | for the delegator | -| reward | uint256 | of the delegator | ### calculateStakePositionPenalty @@ -61,6 +58,29 @@ Returns the penalty and reward that will be burned, if vested stake position is | penalty | uint256 | for the staker | | reward | uint256 | of the staker | +### calculateTotalPositionReward + +```solidity +function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) +``` + +Returns the total reward that is generate for a position + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | The address of the validator | +| delegator | address | The address of the delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| reward | uint256 | for the delegator | + ### claimDelegatorReward ```solidity @@ -195,29 +215,6 @@ Gets delegators's unclaimed rewards including custom rewards -#### Parameters - -| Name | Type | Description | -|---|---|---| -| validator | address | Address of validator | -| delegator | address | Address of delegator | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | - -### getRawDelegatorReward - -```solidity -function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) -``` - -Gets delegators's unclaimed rewards without custom rewards - - - #### Parameters | Name | Type | Description | diff --git a/docs/RewardPool/RewardPool.md b/docs/RewardPool/RewardPool.md index ae1678b4..5be51784 100644 --- a/docs/RewardPool/RewardPool.md +++ b/docs/RewardPool/RewardPool.md @@ -295,13 +295,13 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa | balance | uint256 | undefined | | correction | int256 | undefined | -### calculateDelegatePositionPenalty +### calculatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) +function calculatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty) ``` -Returns the penalty and reward that will be burned, if vested delegate position is active +Returns the penalty that will taken from the delegator, if the position is still active @@ -312,15 +312,12 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | -| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | -| topUpIndex | uint256 | The index when a topup has been made | #### Returns | Name | Type | Description | |---|---|---| | penalty | uint256 | for the delegator | -| reward | uint256 | of the delegator | ### calculateStakePositionPenalty @@ -346,6 +343,29 @@ Returns the penalty and reward that will be burned, if vested stake position is | penalty | uint256 | for the staker | | reward | uint256 | of the staker | +### calculateTotalPositionReward + +```solidity +function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) +``` + +Returns the total reward that is generate for a position + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | The address of the validator | +| delegator | address | The address of the delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| reward | uint256 | for the delegator | + ### claimDelegatorReward ```solidity @@ -571,7 +591,7 @@ Gets delegators's history of the delegated position ### getDelegatorPositionReward ```solidity -function getDelegatorPositionReward(address validator, address delegator, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256) +function getDelegatorPositionReward(address validator, address delegator, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 sumReward) ``` Gets delegators's unclaimed rewards including custom rewards for a position @@ -591,7 +611,7 @@ Gets delegators's unclaimed rewards including custom rewards for a position | Name | Type | Description | |---|---|---| -| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | +| sumReward | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | ### getDelegatorReward @@ -703,7 +723,7 @@ function getRPSValues(address validator, uint256 startEpoch, uint256 endEpoch) e function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) ``` -Gets delegators's unclaimed rewards without custom rewards +Gets delegator's unclaimed rewards without custom rewards diff --git a/docs/RewardPool/RewardPoolBase.md b/docs/RewardPool/RewardPoolBase.md index a31b86c0..230f9e50 100644 --- a/docs/RewardPool/RewardPoolBase.md +++ b/docs/RewardPool/RewardPoolBase.md @@ -10,13 +10,13 @@ the base state variables and functionality needed in different modules that the ## Methods -### calculateDelegatePositionPenalty +### calculatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) +function calculatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty) ``` -Returns the penalty and reward that will be burned, if vested delegate position is active +Returns the penalty that will taken from the delegator, if the position is still active @@ -27,15 +27,12 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | -| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | -| topUpIndex | uint256 | The index when a topup has been made | #### Returns | Name | Type | Description | |---|---|---| | penalty | uint256 | for the delegator | -| reward | uint256 | of the delegator | ### calculateStakePositionPenalty @@ -61,6 +58,29 @@ Returns the penalty and reward that will be burned, if vested stake position is | penalty | uint256 | for the staker | | reward | uint256 | of the staker | +### calculateTotalPositionReward + +```solidity +function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) +``` + +Returns the total reward that is generate for a position + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | The address of the validator | +| delegator | address | The address of the delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| reward | uint256 | for the delegator | + ### claimDelegatorReward ```solidity @@ -195,29 +215,6 @@ Gets delegators's unclaimed rewards including custom rewards -#### Parameters - -| Name | Type | Description | -|---|---|---| -| validator | address | Address of validator | -| delegator | address | Address of delegator | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | - -### getRawDelegatorReward - -```solidity -function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) -``` - -Gets delegators's unclaimed rewards without custom rewards - - - #### Parameters | Name | Type | Description | diff --git a/docs/RewardPool/modules/DelegationRewards.md b/docs/RewardPool/modules/DelegationRewards.md index ff204b72..6a86a644 100644 --- a/docs/RewardPool/modules/DelegationRewards.md +++ b/docs/RewardPool/modules/DelegationRewards.md @@ -193,13 +193,13 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa | balance | uint256 | undefined | | correction | int256 | undefined | -### calculateDelegatePositionPenalty +### calculatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) +function calculatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty) ``` -Returns the penalty and reward that will be burned, if vested delegate position is active +Returns the penalty that will taken from the delegator, if the position is still active @@ -210,15 +210,12 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | -| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | -| topUpIndex | uint256 | The index when a topup has been made | #### Returns | Name | Type | Description | |---|---|---| | penalty | uint256 | for the delegator | -| reward | uint256 | of the delegator | ### calculateStakePositionPenalty @@ -244,6 +241,29 @@ Returns the penalty and reward that will be burned, if vested stake position is | penalty | uint256 | for the staker | | reward | uint256 | of the staker | +### calculateTotalPositionReward + +```solidity +function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) +``` + +Returns the total reward that is generate for a position + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | The address of the validator | +| delegator | address | The address of the delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| reward | uint256 | for the delegator | + ### claimDelegatorReward ```solidity @@ -442,7 +462,7 @@ Gets delegators's history of the delegated position ### getDelegatorPositionReward ```solidity -function getDelegatorPositionReward(address validator, address delegator, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256) +function getDelegatorPositionReward(address validator, address delegator, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 sumReward) ``` Gets delegators's unclaimed rewards including custom rewards for a position @@ -462,7 +482,7 @@ Gets delegators's unclaimed rewards including custom rewards for a position | Name | Type | Description | |---|---|---| -| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | +| sumReward | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | ### getDelegatorReward @@ -574,7 +594,7 @@ function getRPSValues(address validator, uint256 startEpoch, uint256 endEpoch) e function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) ``` -Gets delegators's unclaimed rewards without custom rewards +Gets delegator's unclaimed rewards without custom rewards diff --git a/docs/RewardPool/modules/StakingRewards.md b/docs/RewardPool/modules/StakingRewards.md index 1305090d..fcae9a97 100644 --- a/docs/RewardPool/modules/StakingRewards.md +++ b/docs/RewardPool/modules/StakingRewards.md @@ -193,13 +193,13 @@ function beforeTopUpParams(address, address) external view returns (uint256 rewa | balance | uint256 | undefined | | correction | int256 | undefined | -### calculateDelegatePositionPenalty +### calculatePositionPenalty ```solidity -function calculateDelegatePositionPenalty(address validator, address delegator, uint256 amount, uint256 epochNumber, uint256 topUpIndex) external view returns (uint256 penalty, uint256 reward) +function calculatePositionPenalty(address validator, address delegator, uint256 amount) external view returns (uint256 penalty) ``` -Returns the penalty and reward that will be burned, if vested delegate position is active +Returns the penalty that will taken from the delegator, if the position is still active @@ -210,15 +210,12 @@ Returns the penalty and reward that will be burned, if vested delegate position | validator | address | The address of the validator | | delegator | address | The address of the delegator | | amount | uint256 | The amount that is going to be undelegated | -| epochNumber | uint256 | Epoch where the last claimable reward is distributed We need it because not all rewards are matured at the moment of calling the function | -| topUpIndex | uint256 | The index when a topup has been made | #### Returns | Name | Type | Description | |---|---|---| | penalty | uint256 | for the delegator | -| reward | uint256 | of the delegator | ### calculateStakePositionPenalty @@ -244,6 +241,29 @@ Returns the penalty and reward that will be burned, if vested stake position is | penalty | uint256 | for the staker | | reward | uint256 | of the staker | +### calculateTotalPositionReward + +```solidity +function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) +``` + +Returns the total reward that is generate for a position + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | The address of the validator | +| delegator | address | The address of the delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| reward | uint256 | for the delegator | + ### claimDelegatorReward ```solidity @@ -570,29 +590,6 @@ function getRPSValues(address validator, uint256 startEpoch, uint256 endEpoch) e |---|---|---| | _0 | RPS[] | undefined | -### getRawDelegatorReward - -```solidity -function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) -``` - -Gets delegators's unclaimed rewards without custom rewards - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| validator | address | Address of validator | -| delegator | address | Address of delegator | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | - ### getRoleAdmin ```solidity diff --git a/test/RewardPool/RewardPool.test.ts b/test/RewardPool/RewardPool.test.ts index 5315be1f..4bc07066 100644 --- a/test/RewardPool/RewardPool.test.ts +++ b/test/RewardPool/RewardPool.test.ts @@ -165,7 +165,72 @@ export function RunDelegateClaimTests(): void { } export function RunVestedDelegationRewardsTests(): void { - describe("getDelegatorPositionReward", async function () { + describe("Delegate position rewards", async function () { + it("should get no rewards if the position is still active", async function () { + const { systemValidatorSet, validatorSet, rewardPool } = await loadFixture(this.fixtures.delegatedFixture); + + const validator = this.signers.validators[1]; + const manager = await createManagerAndVest( + validatorSet, + rewardPool, + this.signers.accounts[4], + validator.address, + VESTING_DURATION_WEEKS, + this.minDelegation.mul(100) + ); + + // pass two weeks ahead + await time.increase(WEEK * 2); + + // Commit epochs so rewards to be distributed + await commitEpochs( + systemValidatorSet, + rewardPool, + [this.signers.validators[0], validator], + 10, // number of epochs to commit + this.epochSize + ); + + const managerRewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + validator.address, + manager.address + ); + + expect(managerRewards).to.equal(0); + }); + + it("should generate partial rewards when enter maturing period", async function () { + const { systemValidatorSet, validatorSet, rewardPool, vestManager, delegatedValidator } = await loadFixture( + this.fixtures.weeklyVestedDelegationFixture + ); + + // enter maturing period + await time.increase(WEEK * 1 + 1); + + // Commit epoch so some more rewards are distributed + await commitEpoch( + systemValidatorSet, + rewardPool, + [this.signers.validators[0], delegatedValidator], + this.epochSize + ); + + const managerRewards = await getDelegatorPositionReward( + validatorSet, + rewardPool, + delegatedValidator.address, + vestManager.address + ); + const totalRewards = await rewardPool.calculateTotalPositionReward( + delegatedValidator.address, + vestManager.address + ); + + expect(managerRewards).to.be.lessThan(totalRewards); + }); + it("should have the same rewards if the position size and period are the same", async function () { const { systemValidatorSet, validatorSet, rewardPool } = await loadFixture(this.fixtures.delegatedFixture); @@ -199,18 +264,8 @@ export function RunVestedDelegationRewardsTests(): void { this.epochSize ); - const manager1rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager1.address - ); - const manager2rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager2.address - ); + const manager1rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager1.address); + const manager2rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager2.address); expect(manager1rewards).to.equal(manager2rewards); }); @@ -248,18 +303,8 @@ export function RunVestedDelegationRewardsTests(): void { this.epochSize ); - const manager1rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager1.address - ); - const manager2rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager2.address - ); + const manager1rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager1.address); + const manager2rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager2.address); expect(manager2rewards).to.be.greaterThan(manager1rewards); }); @@ -297,18 +342,8 @@ export function RunVestedDelegationRewardsTests(): void { this.epochSize ); - const manager1rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager1.address - ); - const manager2rewards = await getDelegatorPositionReward( - validatorSet, - rewardPool, - validator.address, - manager2.address - ); + const manager1rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager1.address); + const manager2rewards = await rewardPool.calculateTotalPositionReward(validator.address, manager2.address); expect(manager1rewards).to.be.greaterThan(manager2rewards); }); diff --git a/test/ValidatorSet/Delegation.test.ts b/test/ValidatorSet/Delegation.test.ts index 012c6a3d..bb81d3a8 100644 --- a/test/ValidatorSet/Delegation.test.ts +++ b/test/ValidatorSet/Delegation.test.ts @@ -6,14 +6,7 @@ import * as hre from "hardhat"; // eslint-disable-next-line camelcase import { VestManager__factory } from "../../typechain-types"; import { VESTING_DURATION_WEEKS, WEEK } from "../constants"; -import { - calculatePenalty, - claimPositionRewards, - commitEpoch, - commitEpochs, - getUserManager, - retrieveRPSData, -} from "../helper"; +import { calculatePenalty, claimPositionRewards, commitEpoch, commitEpochs, getUserManager } from "../helper"; import { RunDelegateClaimTests, RunVestedDelegateClaimTests, @@ -462,22 +455,13 @@ export function RunDelegationTests(): void { const position = await rewardPool.delegationPositions(delegatedValidator.address, vestManager.address); const latestTimestamp = hre.ethers.BigNumber.from(await time.latest()); - // prepare params for call - const { epochNum, topUpIndex } = await retrieveRPSData( - systemValidatorSet, - rewardPool, - delegatedValidator.address, - vestManager.address - ); - // get the penalty and reward from the contract - const { penalty, reward } = await rewardPool.calculateDelegatePositionPenalty( + const penalty = await rewardPool.calculatePositionPenalty( delegatedValidator.address, vestManager.address, - this.minStake, - epochNum, - topUpIndex + this.minStake ); + const reward = await rewardPool.calculateTotalPositionReward(delegatedValidator.address, vestManager.address); // calculate penalty locally const calculatedPenalty = await calculatePenalty(position, latestTimestamp, this.minStake); diff --git a/test/helper.ts b/test/helper.ts index 76425ff3..d4fbfdbf 100644 --- a/test/helper.ts +++ b/test/helper.ts @@ -335,12 +335,7 @@ export async function getDelegatorPositionReward( delegator: string ) { // prepare params for call - const { epochNum: epochNumManager1, topUpIndex: topUpIndexNumManager1 } = await retrieveRPSData( - validatorSet, - rewardPool, - validator, - delegator - ); - - return await rewardPool.getDelegatorPositionReward(validator, delegator, epochNumManager1, topUpIndexNumManager1); + const { epochNum, topUpIndex } = await retrieveRPSData(validatorSet, rewardPool, validator, delegator); + + return await rewardPool.getDelegatorPositionReward(validator, delegator, epochNum, topUpIndex); } From 01ad04307addaeb7d233473821f9ba816cdb0023 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Wed, 17 Apr 2024 14:25:51 +0300 Subject: [PATCH 3/9] return back a todo comment; --- test/ValidatorSet/Delegation.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/ValidatorSet/Delegation.test.ts b/test/ValidatorSet/Delegation.test.ts index bb81d3a8..19d0436c 100644 --- a/test/ValidatorSet/Delegation.test.ts +++ b/test/ValidatorSet/Delegation.test.ts @@ -485,6 +485,25 @@ export function RunDelegationTests(): void { const cutAmount = delegatedBalanceBefore.div(2); const position = await rewardPool.delegationPositions(delegatedValidator.address, vestManager.address); + // Hydra TODO: Create table-driven unit tests with precalculated values to test the exact amounts + // check if amount is properly burned + // const end = position.end; + // const rpsValues = await childValidatorSet.getRPSValues(validator); + // const epochNum = findProperRPSIndex(rpsValues, end); + // const topUpIndex = 0; + // let reward = await childValidatorSet.getDelegatorPositionReward( + // validator, + // manager.address, + // epochNum, + // topUpIndex + // ); + // reward = await childValidatorSet.applyMaxReward(reward); + // const decrease = reward.add(amountToBeBurned); + // await expect(manager.cutVestedDelegatePosition(validator, cutAmount)).to.changeEtherBalance( + // childValidatorSet, + // decrease.mul(-1) + // ); + await liquidToken.connect(vestManagerOwner).approve(vestManager.address, cutAmount); const latestTimestamp = hre.ethers.BigNumber.from(await time.latest()); From 66d90b7c2d4b0a14dcba15f729ef9bf5245bf2c6 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Wed, 17 Apr 2024 14:26:45 +0300 Subject: [PATCH 4/9] update comments --- test/RewardPool/RewardPool.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/RewardPool/RewardPool.test.ts b/test/RewardPool/RewardPool.test.ts index 4bc07066..f7d33b63 100644 --- a/test/RewardPool/RewardPool.test.ts +++ b/test/RewardPool/RewardPool.test.ts @@ -291,7 +291,7 @@ export function RunVestedDelegationRewardsTests(): void { this.minDelegation.mul(100) ); - // pass two weeks ahead + // pass three weeks ahead await time.increase(WEEK * 3); // Commit epochs so rewards to be distributed @@ -330,7 +330,7 @@ export function RunVestedDelegationRewardsTests(): void { this.minDelegation.mul(2) ); - // pass two weeks ahead + // pass five weeks ahead await time.increase(WEEK * 5); // Commit epochs so rewards to be distributed From 97c0852ff61e5531cd49e806d304eaa3e8d73665 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Wed, 17 Apr 2024 15:29:21 +0300 Subject: [PATCH 5/9] delete redundant comment; --- contracts/RewardPool/modules/DelegationRewards.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/RewardPool/modules/DelegationRewards.sol b/contracts/RewardPool/modules/DelegationRewards.sol index 00760ee7..a4154f75 100644 --- a/contracts/RewardPool/modules/DelegationRewards.sol +++ b/contracts/RewardPool/modules/DelegationRewards.sol @@ -58,8 +58,6 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa return _applyCustomReward(reward); } - // vito - /** * @inheritdoc IRewardPool */ @@ -122,7 +120,6 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa } } - // vito /** * @inheritdoc IRewardPool */ From 8665638a52bf82b6718892de9ea7f4eee39aacd2 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Tue, 23 Apr 2024 15:05:27 +0300 Subject: [PATCH 6/9] update docs and comments --- contracts/RewardPool/IRewardPool.sol | 10 +++++++++- contracts/RewardPool/modules/APR.sol | 10 ++++++---- contracts/RewardPool/modules/DelegationRewards.sol | 5 +---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contracts/RewardPool/IRewardPool.sol b/contracts/RewardPool/IRewardPool.sol index 3d476a2b..28d98574 100644 --- a/contracts/RewardPool/IRewardPool.sol +++ b/contracts/RewardPool/IRewardPool.sol @@ -150,6 +150,14 @@ interface IRewardPool { */ function getValidatorReward(address validator) external view returns (uint256); + /** + * @notice Gets delegator's unclaimed rewards without custom rewards + * @param validator Address of validator + * @param delegator Address of delegator + * @return Delegator's unclaimed rewards with validator (in MATIC wei) + */ + function getRawDelegatorReward(address validator, address delegator) external view returns (uint256); + /** * @notice Gets delegators's unclaimed rewards including custom rewards * @param validator Address of validator @@ -211,7 +219,7 @@ interface IRewardPool { ) external view returns (uint256 penalty); /** - * @notice Returns the total reward that is generate for a position + * @notice Returns the total reward that is generated for a position * @param validator The address of the validator * @param delegator The address of the delegator * @return reward for the delegator diff --git a/contracts/RewardPool/modules/APR.sol b/contracts/RewardPool/modules/APR.sol index fc9d3d3d..c7cbb3d3 100644 --- a/contracts/RewardPool/modules/APR.sol +++ b/contracts/RewardPool/modules/APR.sol @@ -96,10 +96,12 @@ contract APR is Initializable, AccessControl { return 1e18; } - /// @notice Function that applies the base APR to a given reward - /// @dev Denominator is used because we should work with numbers with floating point - /// @param reward The reward to which we gonna apply the base APR - /// @return The reward with the applied APR + /** + * @notice Function that calculates the end reward for a user (without vesting bonuses) based on the pool reward index. + * @dev Denominator is used because we should work with numbers with floating point + * @param reward index The reward to which we gonna apply the base APR + * @dev The reward with the applied APR + */ function _applyCustomReward(uint256 reward) internal view returns (uint256) { return _applyBaseAPR(reward) / EPOCHS_YEAR; } diff --git a/contracts/RewardPool/modules/DelegationRewards.sol b/contracts/RewardPool/modules/DelegationRewards.sol index a4154f75..628b999b 100644 --- a/contracts/RewardPool/modules/DelegationRewards.sol +++ b/contracts/RewardPool/modules/DelegationRewards.sol @@ -371,10 +371,7 @@ abstract contract DelegationRewards is RewardPoolBase, Vesting, RewardsWithdrawa // _______________ Public functions _______________ /** - * @notice Gets delegator's unclaimed rewards without custom rewards - * @param validator Address of validator - * @param delegator Address of delegator - * @return Delegator's unclaimed rewards with validator (in MATIC wei) + * @inheritdoc IRewardPool */ function getRawDelegatorReward(address validator, address delegator) public view returns (uint256) { DelegationPool storage delegation = delegationPools[validator]; From 5424c657edfb9305517e51c1206d16e61f97d0c8 Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Tue, 23 Apr 2024 15:06:57 +0300 Subject: [PATCH 7/9] compile contracts --- docs/RewardPool/IRewardPool.md | 25 +++++++++++++++++++- docs/RewardPool/RewardPool.md | 2 +- docs/RewardPool/RewardPoolBase.md | 25 +++++++++++++++++++- docs/RewardPool/modules/DelegationRewards.md | 2 +- docs/RewardPool/modules/StakingRewards.md | 25 +++++++++++++++++++- 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/docs/RewardPool/IRewardPool.md b/docs/RewardPool/IRewardPool.md index 45a2a71a..7907a93e 100644 --- a/docs/RewardPool/IRewardPool.md +++ b/docs/RewardPool/IRewardPool.md @@ -64,7 +64,7 @@ Returns the penalty and reward that will be burned, if vested stake position is function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) ``` -Returns the total reward that is generate for a position +Returns the total reward that is generated for a position @@ -215,6 +215,29 @@ Gets delegators's unclaimed rewards including custom rewards +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | Address of validator | +| delegator | address | Address of delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | + +### getRawDelegatorReward + +```solidity +function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) +``` + +Gets delegator's unclaimed rewards without custom rewards + + + #### Parameters | Name | Type | Description | diff --git a/docs/RewardPool/RewardPool.md b/docs/RewardPool/RewardPool.md index 5be51784..4d6a2326 100644 --- a/docs/RewardPool/RewardPool.md +++ b/docs/RewardPool/RewardPool.md @@ -349,7 +349,7 @@ Returns the penalty and reward that will be burned, if vested stake position is function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) ``` -Returns the total reward that is generate for a position +Returns the total reward that is generated for a position diff --git a/docs/RewardPool/RewardPoolBase.md b/docs/RewardPool/RewardPoolBase.md index 230f9e50..84d2c424 100644 --- a/docs/RewardPool/RewardPoolBase.md +++ b/docs/RewardPool/RewardPoolBase.md @@ -64,7 +64,7 @@ Returns the penalty and reward that will be burned, if vested stake position is function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) ``` -Returns the total reward that is generate for a position +Returns the total reward that is generated for a position @@ -215,6 +215,29 @@ Gets delegators's unclaimed rewards including custom rewards +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | Address of validator | +| delegator | address | Address of delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | + +### getRawDelegatorReward + +```solidity +function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) +``` + +Gets delegator's unclaimed rewards without custom rewards + + + #### Parameters | Name | Type | Description | diff --git a/docs/RewardPool/modules/DelegationRewards.md b/docs/RewardPool/modules/DelegationRewards.md index 6a86a644..5da330f1 100644 --- a/docs/RewardPool/modules/DelegationRewards.md +++ b/docs/RewardPool/modules/DelegationRewards.md @@ -247,7 +247,7 @@ Returns the penalty and reward that will be burned, if vested stake position is function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) ``` -Returns the total reward that is generate for a position +Returns the total reward that is generated for a position diff --git a/docs/RewardPool/modules/StakingRewards.md b/docs/RewardPool/modules/StakingRewards.md index fcae9a97..71745a94 100644 --- a/docs/RewardPool/modules/StakingRewards.md +++ b/docs/RewardPool/modules/StakingRewards.md @@ -247,7 +247,7 @@ Returns the penalty and reward that will be burned, if vested stake position is function calculateTotalPositionReward(address validator, address delegator) external view returns (uint256 reward) ``` -Returns the total reward that is generate for a position +Returns the total reward that is generated for a position @@ -590,6 +590,29 @@ function getRPSValues(address validator, uint256 startEpoch, uint256 endEpoch) e |---|---|---| | _0 | RPS[] | undefined | +### getRawDelegatorReward + +```solidity +function getRawDelegatorReward(address validator, address delegator) external view returns (uint256) +``` + +Gets delegator's unclaimed rewards without custom rewards + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| validator | address | Address of validator | +| delegator | address | Address of delegator | + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | uint256 | Delegator's unclaimed rewards with validator (in MATIC wei) | + ### getRoleAdmin ```solidity From 0776851583bc40a296e3453615e6047c802ffa6a Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Tue, 23 Apr 2024 15:34:41 +0300 Subject: [PATCH 8/9] update natspec; --- contracts/RewardPool/modules/APR.sol | 2 +- contracts/RewardPool/modules/Vesting.sol | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/contracts/RewardPool/modules/APR.sol b/contracts/RewardPool/modules/APR.sol index c7cbb3d3..150a3236 100644 --- a/contracts/RewardPool/modules/APR.sol +++ b/contracts/RewardPool/modules/APR.sol @@ -98,7 +98,7 @@ contract APR is Initializable, AccessControl { /** * @notice Function that calculates the end reward for a user (without vesting bonuses) based on the pool reward index. - * @dev Denominator is used because we should work with numbers with floating point + * @dev Denominator is used because we should work with floating-point numbers * @param reward index The reward to which we gonna apply the base APR * @dev The reward with the applied APR */ diff --git a/contracts/RewardPool/modules/Vesting.sol b/contracts/RewardPool/modules/Vesting.sol index a1e4071c..5e7ccb13 100644 --- a/contracts/RewardPool/modules/Vesting.sol +++ b/contracts/RewardPool/modules/Vesting.sol @@ -140,10 +140,12 @@ abstract contract Vesting is APR { historyRPS[validator][epochNumber] = RPS({value: uint192(rewardPerShare), timestamp: uint64(block.timestamp)}); } - /// @notice Function that applies the custom factors - base APR, vest bonus and rsi bonus - /// @dev Denominator is used because we should work with numbers with floating point - /// @param reward The reward to which we gonna apply the custom APR - /// @return The reward with the applied APR + /** + * @notice Function that applies the custom factors - base APR, vest bonus and rsi bonus + * @dev Denominator is used because we should work with floating-point numbers + * @param reward index The reward to which we gonna apply the custom APR + * @dev The reward with the applied APR + */ function _applyCustomReward( VestingPosition memory position, uint256 reward, From aa9e027e82866bcdcf68df50c5b7738027ef761b Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Tue, 23 Apr 2024 15:53:18 +0300 Subject: [PATCH 9/9] update natspec docs --- contracts/RewardPool/modules/Vesting.sol | 32 ++++++++++++++++-------- contracts/ValidatorSet/ValidatorSet.sol | 6 +++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/contracts/RewardPool/modules/Vesting.sol b/contracts/RewardPool/modules/Vesting.sol index 5e7ccb13..5afa44a4 100644 --- a/contracts/RewardPool/modules/Vesting.sol +++ b/contracts/RewardPool/modules/Vesting.sol @@ -37,26 +37,36 @@ error NotVestingManager(); abstract contract Vesting is APR { using VestingPositionLib for VestingPosition; - /// @notice A constant for the calculation of the weeks left of a vesting period - /// @dev Representing a week in seconds - 1 + /** + * @notice A constant for the calculation of the weeks left of a vesting period + * @dev Representing a week in seconds - 1 + */ uint256 private constant WEEK_MINUS_SECOND = 604799; /// @notice The vesting positions for every validator mapping(address => VestingPosition) public positions; - /// @notice The vesting positions for every delegator. - /// @dev Validator => Delegator => VestingPosition + /** + * @notice The vesting positions for every delegator + * @dev Validator => Delegator => VestingPosition + */ mapping(address => mapping(address => VestingPosition)) public delegationPositions; - /// @notice Keeps the history of the RPS for the validators - /// @dev This is used to keep the history RPS in order to calculate properly the rewards + /** + * @notice Keeps the history of the RPS for the validators + * @dev This is used to keep the history RPS in order to calculate properly the rewards + */ mapping(address => mapping(uint256 => RPS)) public historyRPS; /// @notice Keeps the rewards history of the validators mapping(address => ValRewardHistory[]) public valRewardHistory; - /// @notice Historical Validator Delegation Pool's Params per delegator - /// @dev Validator => Delegator => Top-up data + /** + * @notice Historical Validator Delegation Pool's Params per delegator + * @dev Validator => Delegator => Top-up data + */ mapping(address => mapping(address => DelegationPoolParams[])) public delegationPoolParamsHistory; - /// @dev Keep the account parameters before the top-up, so we can separately calculate the rewards made before a top-up is made - /// @dev This is because we need to apply the RSI bonus to the rewards made before the top-up - /// @dev and not apply the RSI bonus to the rewards made after the top-up + /** + * @dev Keep the account parameters before the top-up, so we can separately calculate the rewards made before a top-up is made + * This is because we need to apply the RSI bonus to the rewards made before the top-up + * and not apply the RSI bonus to the rewards made after the top-up + */ mapping(address => mapping(address => RewardParams)) public beforeTopUpParams; // _______________ External functions _______________ diff --git a/contracts/ValidatorSet/ValidatorSet.sol b/contracts/ValidatorSet/ValidatorSet.sol index f3e695ba..5e0f711f 100644 --- a/contracts/ValidatorSet/ValidatorSet.sol +++ b/contracts/ValidatorSet/ValidatorSet.sol @@ -89,8 +89,10 @@ contract ValidatorSet is ValidatorSetBase, System, AccessControl, PowerExponent, emit NewEpoch(id, epoch.startBlock, epoch.endBlock, epoch.epochRoot); } - /// @notice Get the validator by its address - /// @param validatorAddress address + /** + * @notice Get the validator by its address + * @param validatorAddress address + */ function getValidator( address validatorAddress )