Skip to content

Commit

Permalink
integrate the new RewardWallet contract
Browse files Browse the repository at this point in the history
remove the payability of Hydra distributeRewardsFor function;
remove all the burning of the remaining reward;
integrated the RewardWallet contract for the distribution of the rewards;
adapt the tests to the changes;
delete some todo comments because their were covered by the changes;
  • Loading branch information
Vitomir2 committed Jul 17, 2024
1 parent b9b4a41 commit 6f20f2b
Show file tree
Hide file tree
Showing 27 changed files with 404 additions and 272 deletions.
2 changes: 0 additions & 2 deletions contracts/HydraChain/HydraChain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import {IHydraChain} from "./IHydraChain.sol";
import {Uptime} from "./modules/ValidatorManager/IValidatorManager.sol";
import {Epoch} from "./IHydraChain.sol";

// TODO: setup use of reward account that would handle the amounts of rewards

contract HydraChain is IHydraChain, Ownable2StepUpgradeable, ValidatorManager, Inspector, PowerExponent {
using ArraysUpgradeable for uint256[];

Expand Down
16 changes: 13 additions & 3 deletions contracts/HydraDelegation/Delegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@ import {Withdrawal} from "./../common/Withdrawal/Withdrawal.sol";
import {APRCalculatorConnector} from "./../APRCalculator/APRCalculatorConnector.sol";
import {HydraStakingConnector} from "./../HydraStaking/HydraStakingConnector.sol";
import {HydraChainConnector} from "./../HydraChain/HydraChainConnector.sol";
import {RewardWalletConnector} from "./../RewardWallet/RewardWalletConnector.sol";
import {DelegationPoolLib} from "./DelegationPoolLib.sol";
import {IDelegation, DelegationPool} from "./IDelegation.sol";

contract Delegation is IDelegation, Governed, Withdrawal, APRCalculatorConnector, HydraStakingConnector, HydraChainConnector {
contract Delegation is
IDelegation,
Governed,
Withdrawal,
APRCalculatorConnector,
HydraStakingConnector,
HydraChainConnector,
RewardWalletConnector
{
using DelegationPoolLib for DelegationPool;

/// @notice A constant for the minimum delegation limit
uint256 public constant MIN_DELEGATION_LIMIT = 1 ether;

/// @notice Keeps the delegation pools
mapping(address => DelegationPool) public delegationPools;

// @note maybe this must be part of the HydraChain
/// @notice The minimum delegation amount to be delegated
uint256 public minDelegation;
Expand Down Expand Up @@ -210,8 +220,8 @@ contract Delegation is IDelegation, Governed, Withdrawal, APRCalculatorConnector
uint256 reward = aprCalculatorContract.applyBaseAPR(rewardIndex);
if (reward == 0) return;

emit DelegatorRewardsClaimed(staker, delegator, reward);
rewardWalletContract.distributeReward(delegator, reward);

_withdraw(delegator, reward);
emit DelegatorRewardsClaimed(staker, delegator, reward);
}
}
7 changes: 5 additions & 2 deletions contracts/HydraDelegation/HydraDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LiquidDelegation} from "./modules/LiquidDelegation/LiquidDelegation.sol"
import {VestedDelegation} from "./modules/VestedDelegation/VestedDelegation.sol";
import {APRCalculatorConnector} from "./../APRCalculator/APRCalculatorConnector.sol";
import {HydraStakingConnector} from "./../HydraStaking/HydraStakingConnector.sol";
import {RewardWalletConnector} from "./../RewardWallet/RewardWalletConnector.sol";
import {IHydraDelegation} from "./IHydraDelegation.sol";
import {StakerInit} from "./../HydraStaking/IHydraStaking.sol";

Expand All @@ -15,6 +16,7 @@ contract HydraDelegation is
System,
APRCalculatorConnector,
HydraStakingConnector,
RewardWalletConnector,
Delegation,
LiquidDelegation,
VestedDelegation
Expand All @@ -34,13 +36,14 @@ contract HydraDelegation is
address aprCalculatorAddr,
address hydraStakingAddr,
address hydraChainAddr,
address vestingManagerFactoryAddr
address vestingManagerFactoryAddr,
address rewardWalletConnectorAddr
) external initializer onlySystemCall {
__APRCalculatorConnector_init(aprCalculatorAddr);
__HydraStakingConnector_init(hydraStakingAddr);
__Delegation_init(governance);
__LiquidDelegation_init(liquidToken);
__VestedDelegation_init(vestingManagerFactoryAddr, hydraChainAddr);
__VestedDelegation_init(vestingManagerFactoryAddr, hydraChainAddr, rewardWalletConnectorAddr);

_initialize(initialStakers, initialCommission);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import {VestingPosition} from "./../../../common/Vesting/IVesting.sol";
import {IVestedDelegation, DelegationPoolParams, RPS} from "./IVestedDelegation.sol";
import {HydraChainConnector} from "./../../../HydraChain/HydraChainConnector.sol";
import {VestingManagerFactoryConnector} from "./../../../VestingManager/VestingManagerFactoryConnector.sol";
import {RewardWalletConnector} from "./../../../RewardWallet/RewardWalletConnector.sol";

contract VestedDelegation is
IVestedDelegation,
Governed,
Withdrawal,
APRCalculatorConnector,
HydraChainConnector,
RewardWalletConnector,
Delegation,
VestingManagerFactoryConnector
{
Expand All @@ -41,11 +43,6 @@ contract VestedDelegation is
*/
uint256 private constant WEEK_MINUS_SECOND = 604799;

/**
* @notice The threshold for the maximum number of allowed balance changes
* @dev We are using this to restrict unlimited changes of the balance (delegationPoolParamsHistory)
*/
uint256 public balanceChangeThreshold;
/**
* @notice Keeps the history of the RPS for the stakers
* @dev This is used to keep the history RPS in order to calculate properly the rewards
Expand All @@ -56,14 +53,14 @@ contract VestedDelegation is

// _______________ Initializer _______________

function __VestedDelegation_init(address _vestingManagerFactoryAddr, address _hydraChainAddr) internal onlyInitializing {
function __VestedDelegation_init(
address _vestingManagerFactoryAddr,
address _hydraChainAddr,
address _rewardWalletAddr
) internal onlyInitializing {
__VestingManagerFactoryConnector_init(_vestingManagerFactoryAddr);
__HydraChainConnector_init(_hydraChainAddr);
__VestedDelegation_init_unchained();
}

function __VestedDelegation_init_unchained() internal onlyInitializing {
balanceChangeThreshold = 32;
__RewardWalletConnector_init(_rewardWalletAddr);
}

// _______________ Modifiers _______________
Expand Down Expand Up @@ -113,11 +110,7 @@ contract VestedDelegation is
/**
* @inheritdoc IVestedDelegation
*/
function getRPSValues(
address staker,
uint256 startEpoch,
uint256 endEpoch
) external view returns (RPS[] memory) {
function getRPSValues(address staker, uint256 startEpoch, uint256 endEpoch) external view returns (RPS[] memory) {
require(startEpoch <= endEpoch, "Invalid args");

RPS[] memory values = new RPS[](endEpoch - startEpoch + 1);
Expand Down Expand Up @@ -237,11 +230,10 @@ contract VestedDelegation is
uint256 delegatedAmount = delegation.balanceOf(msg.sender);
uint256 delegatedAmountLeft = delegatedAmount - amount;
uint256 penalty;
uint256 fullReward;
if (position.isActive()) {
penalty = _calcPenalty(position, amount);
// apply the max Vesting bonus, because the full reward must be burned
fullReward = aprCalculatorContract.applyMaxReward(delegation.claimRewards(msg.sender));
// claim rewards to increase the claimedRewards because the delegator lose its rewards
delegation.claimRewards(msg.sender);

// if position is closed when active, we delete the vesting data
if (delegatedAmountLeft == 0) {
Expand All @@ -263,7 +255,7 @@ contract VestedDelegation is

_undelegate(staker, msg.sender, amount);
uint256 amountAfterPenalty = amount - penalty;
_burnAmount(penalty + fullReward);
_burnAmount(penalty);
_registerWithdrawal(msg.sender, amountAfterPenalty);

emit PositionCut(msg.sender, staker, amountAfterPenalty);
Expand All @@ -272,19 +264,13 @@ contract VestedDelegation is
/**
* @inheritdoc IVestedDelegation
*/
function claimPositionReward(
address staker,
address to,
uint256 epochNumber,
uint256 balanceChangeIndex
) external {
function claimPositionReward(address staker, address to, uint256 epochNumber, uint256 balanceChangeIndex) external {
VestingPosition memory position = vestedDelegationPositions[staker][msg.sender];
if (_noRewardConditions(position)) {
return;
}

uint256 sumReward;
uint256 sumMaxReward;
DelegationPool storage delegationPool = delegationPools[staker];

// distribute the proper vesting reward
Expand All @@ -296,28 +282,19 @@ contract VestedDelegation is
);

uint256 reward = delegationPool.claimRewards(msg.sender, epochRPS, balance, correction);
uint256 maxReward = aprCalculatorContract.applyMaxReward(reward);
reward = _applyVestingAPR(position, reward, true);
sumReward += reward;
sumMaxReward += maxReward;

// 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.claimRewards(msg.sender);
uint256 maxAdditionalReward = aprCalculatorContract.applyMaxReward(additionalReward);
additionalReward = aprCalculatorContract.applyBaseAPR(additionalReward);
sumReward += additionalReward;
sumMaxReward += maxAdditionalReward;
}

uint256 remainder = sumMaxReward - sumReward;
if (remainder > 0) {
_burnAmount(remainder);
}

if (sumReward == 0) return;

_withdraw(to, sumReward);
rewardWalletContract.distributeReward(to, sumReward);

emit PositionRewardClaimed(msg.sender, staker, sumReward);
}
Expand Down Expand Up @@ -345,7 +322,7 @@ contract VestedDelegation is

uint256 duration = durationWeeks * 1 weeks;
delete delegationPoolParamsHistory[staker][msg.sender];
// TODO: calculate end of period instead of write in in the cold storage. It is cheaper
// TODO: calculate end of period instead of write in the cold storage. It is cheaper
vestedDelegationPositions[staker][msg.sender] = VestingPosition({
duration: duration,
start: block.timestamp,
Expand Down Expand Up @@ -396,16 +373,6 @@ contract VestedDelegation is
return false;
}

// TODO: Consider deleting it as we shouldn't be getting into that case
/**
* @notice Checks if the balance changes exceeds the threshold
* @param staker Validator to delegate to
* @param delegator Delegator that has delegated
*/
function isBalanceChangeThresholdExceeded(address staker, address delegator) public view returns (bool) {
return delegationPoolParamsHistory[staker][delegator].length > balanceChangeThreshold;
}

/**
* @notice Check if the new position that the user wants to swap to is available for the swap
* @dev Available positions one that is not active, not maturing and doesn't have any left balance or rewards
Expand Down Expand Up @@ -544,21 +511,12 @@ contract VestedDelegation is
* @param delegator Address of the delegator
* @param params Delegation pool params
*/
function _saveAccountParamsChange(
address staker,
address delegator,
DelegationPoolParams memory params
) private {
function _saveAccountParamsChange(address staker, address delegator, DelegationPoolParams memory params) private {
if (isBalanceChangeMade(staker, delegator, params.epochNum)) {
// balance can be changed only once per epoch
revert DelegateRequirement({src: "_saveAccountParamsChange", msg: "BALANCE_CHANGE_ALREADY_MADE"});
}

if (isBalanceChangeThresholdExceeded(staker, delegator)) {
// maximum amount of balance changes exceeded
revert DelegateRequirement({src: "_saveAccountParamsChange", msg: "BALANCE_CHANGES_EXCEEDED"});
}

delegationPoolParamsHistory[staker][delegator].push(params);
}

Expand Down
24 changes: 10 additions & 14 deletions contracts/HydraStaking/HydraStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
pragma solidity 0.8.17;

import {Staking} from "./Staking.sol";
import {System} from "./../common/System/System.sol";
import {Unauthorized, StakeRequirement} from "./../common/Errors.sol";
import {IHydraStaking, StakerInit} from "./IHydraStaking.sol";
import {LiquidStaking} from "./modules/LiquidStaking/LiquidStaking.sol";
import {VestedStaking} from "./modules/VestedStaking/VestedStaking.sol";
import {DelegatedStaking} from "./modules/DelegatedStaking/DelegatedStaking.sol";
import {StateSyncStaking} from "./modules/StateSyncStaking/StateSyncStaking.sol";
import {HydraChainConnector} from "./../HydraChain/HydraChainConnector.sol";
import {PenalizeableStaking} from "./modules/PenalizeableStaking/PenalizeableStaking.sol";
import {IHydraStaking, StakerInit} from "./IHydraStaking.sol";
import {System} from "./../common/System/System.sol";
import {Unauthorized, StakeRequirement} from "./../common/Errors.sol";
import {HydraChainConnector} from "./../HydraChain/HydraChainConnector.sol";
import {RewardWalletConnector} from "./../RewardWallet/RewardWalletConnector.sol";
import {PenalizedStakeDistribution} from "./modules/PenalizeableStaking/IPenalizeableStaking.sol";
import {Uptime} from "./../HydraChain/modules/ValidatorManager/IValidatorManager.sol";
import {Governed} from "./../common/Governed/Governed.sol";
Expand All @@ -22,6 +23,7 @@ contract HydraStaking is
IHydraStaking,
System,
HydraChainConnector,
RewardWalletConnector,
Staking,
VestedStaking,
StateSyncStaking,
Expand All @@ -45,10 +47,11 @@ contract HydraStaking is
address newLiquidToken,
address hydraChainAddr,
address aprCalculatorAddr,
address delegationContractAddr
address delegationContractAddr,
address rewardWalletContractAddr
) external initializer onlySystemCall {
__HydraChainConnector_init(hydraChainAddr);
__Staking_init(newMinStake, aprCalculatorAddr, governance);
__Staking_init(newMinStake, aprCalculatorAddr, rewardWalletContractAddr, governance);
__LiquidStaking_init(newLiquidToken);
__DelegatedStaking_init(delegationContractAddr);

Expand All @@ -71,7 +74,7 @@ contract HydraStaking is
uint256 epochId,
Uptime[] calldata uptime,
uint256 epochSize
) external payable onlySystemCall {
) external onlySystemCall {
require(distributedRewardPerEpoch[epochId] == 0, "REWARD_ALREADY_DISTRIBUTED");

uint256 totalBlocks = hydraChainContract.totalBlocks(epochId);
Expand Down Expand Up @@ -183,13 +186,6 @@ contract HydraStaking is
return super._claimStakingRewards(staker);
}

// TODO: The unrealized potential staking reward for all stakers must be burned at the end because
// HYDRA is minted for the full potential staking reward but only part of it will go as a reward for the stakers
// we have to handle the other part
// Other option and maybe better because it will simplify the logic and will decrease computation on both node and contract
// is having a reward wallet that will have close to full hydra balance all the time.
// We will use it when the actual end reward will be transfered to the recipient (staker delegator).

/**
* @notice Distributes the staking rewards for the staker.
* @param account The account to distribute the rewards for
Expand Down
4 changes: 2 additions & 2 deletions contracts/HydraStaking/IHydraStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ struct StakerInit {
interface IHydraStaking is IDelegatedStaking, IStaking, ILiquidStaking, IPenalizeableStaking {
/**
* @notice Distributes rewards for the given epoch
* @dev Transfers funds from sender to this contract
* @dev The function updates the rewards in the Staking and Delegation contracts' state
* @param epochId The epoch number
* @param uptime uptime data for every validator (staker)
* @param epochSize Number of blocks per epoch
*/
function distributeRewardsFor(uint256 epochId, Uptime[] calldata uptime, uint256 epochSize) external payable;
function distributeRewardsFor(uint256 epochId, Uptime[] calldata uptime, uint256 epochSize) external;

// _______________ Public functions _______________

Expand Down
11 changes: 7 additions & 4 deletions contracts/HydraStaking/Staking.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {IStaking, StakingReward} from "./IStaking.sol";
import {Governed} from "./../common/Governed/Governed.sol";
import {Withdrawal} from "./../common/Withdrawal/Withdrawal.sol";
import {APRCalculatorConnector} from "./../APRCalculator/APRCalculatorConnector.sol";
import {Unauthorized} from "./../common/Errors.sol";
import {IStaking, StakingReward} from "./IStaking.sol";
import {APRCalculatorConnector} from "./../APRCalculator/APRCalculatorConnector.sol";
import {RewardWalletConnector} from "./../RewardWallet/RewardWalletConnector.sol";

contract Staking is IStaking, Governed, Withdrawal, APRCalculatorConnector {
contract Staking is IStaking, Governed, Withdrawal, APRCalculatorConnector, RewardWalletConnector {
/// @notice A constant for the minimum stake limit
uint256 public constant MIN_STAKE_LIMIT = 1 ether;

Expand All @@ -23,11 +24,13 @@ contract Staking is IStaking, Governed, Withdrawal, APRCalculatorConnector {
function __Staking_init(
uint256 _newMinStake,
address _aprCalculatorAddr,
address _rewardWalletAddr,
address _governance
) internal onlyInitializing {
__Governed_init(_governance);
__Withdrawal_init(_governance);
__APRCalculatorConnector_init(_aprCalculatorAddr);
__RewardWalletConnector_init(_rewardWalletAddr);
__Staking_init_unchained(_newMinStake);
}

Expand Down Expand Up @@ -87,7 +90,7 @@ contract Staking is IStaking, Governed, Withdrawal, APRCalculatorConnector {
* @inheritdoc IStaking
*/
function claimStakingRewards() public {
_withdraw(msg.sender, _claimStakingRewards(msg.sender));
rewardWalletContract.distributeReward(msg.sender, _claimStakingRewards(msg.sender));
}

// _______________ Internal functions _______________
Expand Down
Loading

0 comments on commit 6f20f2b

Please sign in to comment.