Skip to content

Commit

Permalink
276 fix staking claim rewards collect tokens (#80)
Browse files Browse the repository at this point in the history
* claim rewards before vesting

* update Vesting and VestedStaking

* add testing
  • Loading branch information
SamBorisov authored Nov 13, 2024
1 parent 864333b commit 2918432
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 32 deletions.
14 changes: 2 additions & 12 deletions contracts/HydraDelegation/HydraDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ contract HydraDelegation is

/// @notice A constant for the maximum comission a validator can receive from the delegator's rewards
uint256 public constant MAX_COMMISSION = 100;
uint256 public constant DENOMINATOR = 10000;

/// A fraction's numerator representing the rate
/// at which the liquidity tokens' distribution is decreased on a weekly basis
uint256 public vestingLiquidityDecreasePerWeek;
mapping(address => uint256) public delegationCommissionPerStaker;

// _______________ Initializer _______________
Expand All @@ -58,7 +54,6 @@ contract HydraDelegation is
}

function _initialize(StakerInit[] calldata initialStakers, uint256 initialCommission) private {
vestingLiquidityDecreasePerWeek = 133; // 0.0133
for (uint256 i = 0; i < initialStakers.length; i++) {
_setCommission(initialStakers[i].addr, initialCommission);
}
Expand Down Expand Up @@ -119,9 +114,8 @@ contract HydraDelegation is
*/
function _distributeTokens(address staker, address account, uint256 amount) internal virtual override {
VestingPosition memory position = vestedDelegationPositions[staker][msg.sender];
if (_isDelegateWithVesting(position)) {
uint256 positionDurationInWeeks = position.duration / 1 weeks;
uint256 debt = (amount * positionDurationInWeeks * vestingLiquidityDecreasePerWeek) / DENOMINATOR;
if (_isOpeningPosition(position)) {
uint256 debt = _calculatePositionDebt(amount, position.duration);
liquidityDebts[account] -= debt.toInt256Safe(); // Add negative debt
amount -= debt;
}
Expand Down Expand Up @@ -177,10 +171,6 @@ contract HydraDelegation is
emit CommissionUpdated(staker, newCommission);
}

function _isDelegateWithVesting(VestingPosition memory position) private view returns (bool) {
return position.start == block.timestamp;
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gap;
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ abstract contract VestedDelegation is
) internal onlyInitializing {
__VestingManagerFactoryConnector_init(_vestingManagerFactoryAddr);
__HydraChainConnector_init(_hydraChainAddr);
__Vesting_init();
__VestedDelegation_init_unchained();
}

Expand Down
24 changes: 21 additions & 3 deletions contracts/HydraStaking/HydraStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.17;

import {System} from "../common/System/System.sol";
import {SafeMathUint} from "./../common/libs/SafeMathUint.sol";
import {VestingPosition} from "../common/Vesting/IVesting.sol";
import {Uptime} from "../HydraChain/modules/ValidatorManager/IValidatorManager.sol";
import {RewardWalletConnector} from "../RewardWallet/RewardWalletConnector.sol";
import {LiquidStaking} from "./modules/LiquidStaking/LiquidStaking.sol";
Expand Down Expand Up @@ -51,6 +52,7 @@ contract HydraStaking is
__Staking_init(newMinStake, aprCalculatorAddr, rewardWalletAddr, hydraChainAddr, governance);
__LiquidStaking_init(newLiquidToken);
__DelegatedStaking_init(hydraDelegationAddr);
__VestedStaking_init();

_initialize(initialStakers);
}
Expand Down Expand Up @@ -174,7 +176,7 @@ contract HydraStaking is
address staker,
uint256 unstakeAmount
) internal virtual override returns (uint256 stakeLeft, uint256 withdrawAmount) {
// this will call only StateSyncStaking._unstake(), VestedStaking._unstake() and staking._unstake()
// this will call only StateSyncStaking._unstake(), VestedStaking._unstake() and Staking._unstake()
// because this is the order in the Linearization of the inheritance graph
return StateSyncStaking._unstake(staker, unstakeAmount);
}
Expand All @@ -185,9 +187,8 @@ contract HydraStaking is
function _afterPenalizeStakerHook(address staker, uint256 unstakeAmount, uint256 leftForStaker) internal override {
// the unstake amount of liquid tokens must be paid at the time of withdrawal
// but only the leftForStaker will be automatically requested,
// so we have to set the unstake amount - leftForStaker as liquidity debt
// so we have to set the unstake amount - leftForStaker as liquidity debt that must be paid as well
liquidityDebts[staker] += (unstakeAmount - leftForStaker).toInt256Safe();
_syncState(staker);
}

/**
Expand All @@ -214,6 +215,23 @@ contract HydraStaking is
return super._distributeStakingReward(account, rewardIndex);
}

/**
* This function is called to distribute tokens to a staker. If the staker is opening a vested position,
* the amount of liquid tokens distributed is decreased based on the vesting duration. Specifically, the amount is
* reduced by a percentage per week of vesting. The corresponding negative debt is also added to the account's liquidity debts,
* ensuring that the user must return the appropriate decreased amount.
*/
function _distributeTokens(address staker, uint256 amount) internal virtual override {
VestingPosition memory position = vestedStakingPositions[staker];
if (_isOpeningPosition(position)) {
uint256 debt = _calculatePositionDebt(amount, position.duration);
liquidityDebts[staker] -= debt.toInt256Safe(); // Add negative debt
amount -= debt;
}

super._distributeTokens(staker, amount);
}

/**
* @notice Syncs the state of the staker.
* @dev Checks if the staker has no stake
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ abstract contract VestedStaking is IVestedStaking, Staking, Vesting {
*/
mapping(address => StakingRewardsHistory[]) public stakingRewardsHistory;

// _______________ Initializer _______________

// solhint-disable-next-line func-name-mixedcase
function __VestedStaking_init() internal onlyInitializing {
__Vesting_init();
}

// _______________ External functions _______________

/**
Expand All @@ -32,6 +39,8 @@ abstract contract VestedStaking is IVestedStaking, Staking, Vesting {
if (vestedStakingPositions[msg.sender].isInVestingCycle()) {
revert StakeRequirement({src: "stakeWithVesting", msg: "ALREADY_IN_VESTING_CYCLE"});
}
// Claim the rewards before opening a new position, to avoid locking them during vesting cycle
if (unclaimedRewards(msg.sender) != 0) _claimStakingRewards(msg.sender);

uint256 duration = durationWeeks * 1 weeks;
vestedStakingPositions[msg.sender] = VestingPosition({
Expand Down
36 changes: 36 additions & 0 deletions contracts/common/Vesting/Vesting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,29 @@ abstract contract Vesting is APRCalculatorConnector {

error FailedToBurnAmount();

uint256 public constant DENOMINATOR = 10000;
/**
* @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;

/// A fraction's numerator representing the rate
/// at which the liquidity tokens' distribution is decreased on a weekly basis
uint256 public vestingLiquidityDecreasePerWeek;

// _______________ Initializer _______________

// solhint-disable-next-line func-name-mixedcase
function __Vesting_init() internal onlyInitializing {
__Vesting_init_unchainded();
}

// solhint-disable-next-line func-name-mixedcase
function __Vesting_init_unchainded() internal onlyInitializing {
vestingLiquidityDecreasePerWeek = 133; // 0.0133
}

// _______________ Internal functions _______________

/**
Expand Down Expand Up @@ -66,6 +83,25 @@ abstract contract Vesting is APRCalculatorConnector {
return (reward * bonus) / divider;
}

/**
* @notice Function that calculates the debt vesting position
* @param amount The amount of tokens for the position
* @param duration The duration of the vesting position
* @return debt The debt of the vesting position
*/
function _calculatePositionDebt(uint256 amount, uint256 duration) internal view returns (uint256 debt) {
uint256 positionDurationInWeeks = duration / 1 weeks;
debt = (amount * positionDurationInWeeks * vestingLiquidityDecreasePerWeek) / DENOMINATOR;
}

/**
* Returns whether if we have just opened a vesting position
* @param position The vesting position of the account
*/
function _isOpeningPosition(VestingPosition memory position) internal view returns (bool) {
return position.start == block.timestamp;
}

// slither-disable-next-line unused-state,naming-convention
uint256[50] private __gap;
}
34 changes: 34 additions & 0 deletions docs/HydraDelegation/modules/VestedDelegation/VestedDelegation.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ function DEFAULT_ADMIN_ROLE() external view returns (bytes32)
|---|---|---|
| _0 | bytes32 | undefined |

### DENOMINATOR

```solidity
function DENOMINATOR() external view returns (uint256)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### MIN_DELEGATION_LIMIT

```solidity
Expand Down Expand Up @@ -929,6 +946,23 @@ The vesting positions for every delegator
| vestBonus | uint256 | undefined |
| rsiBonus | uint256 | undefined |

### vestingLiquidityDecreasePerWeek

```solidity
function vestingLiquidityDecreasePerWeek() external view returns (uint256)
```

A fraction&#39;s numerator representing the rate at which the liquidity tokens&#39; distribution is decreased on a weekly basis




#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### vestingManagerFactoryContract

```solidity
Expand Down
34 changes: 34 additions & 0 deletions docs/HydraStaking/HydraStaking.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ function DEFAULT_ADMIN_ROLE() external view returns (bytes32)
|---|---|---|
| _0 | bytes32 | undefined |

### DENOMINATOR

```solidity
function DENOMINATOR() external view returns (uint256)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### MIN_STAKE_LIMIT

```solidity
Expand Down Expand Up @@ -1077,6 +1094,23 @@ The stakers&#39; vesting positions
| vestBonus | uint256 | undefined |
| rsiBonus | uint256 | undefined |

### vestingLiquidityDecreasePerWeek

```solidity
function vestingLiquidityDecreasePerWeek() external view returns (uint256)
```

A fraction&#39;s numerator representing the rate at which the liquidity tokens&#39; distribution is decreased on a weekly basis




#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### withdraw

```solidity
Expand Down
34 changes: 34 additions & 0 deletions docs/HydraStaking/modules/VestedStaking/VestedStaking.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ function DEFAULT_ADMIN_ROLE() external view returns (bytes32)
|---|---|---|
| _0 | bytes32 | undefined |

### DENOMINATOR

```solidity
function DENOMINATOR() external view returns (uint256)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### MIN_STAKE_LIMIT

```solidity
Expand Down Expand Up @@ -675,6 +692,23 @@ The stakers&#39; vesting positions
| vestBonus | uint256 | undefined |
| rsiBonus | uint256 | undefined |

### vestingLiquidityDecreasePerWeek

```solidity
function vestingLiquidityDecreasePerWeek() external view returns (uint256)
```

A fraction&#39;s numerator representing the rate at which the liquidity tokens&#39; distribution is decreased on a weekly basis




#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### withdraw

```solidity
Expand Down
34 changes: 34 additions & 0 deletions docs/common/Vesting/Vesting.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ An extension of the Staking contract that enables vesting the stake for a higher

## Methods

### DENOMINATOR

```solidity
function DENOMINATOR() external view returns (uint256)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |

### aprCalculatorContract

```solidity
Expand All @@ -27,6 +44,23 @@ function aprCalculatorContract() external view returns (contract IAPRCalculator)
|---|---|---|
| _0 | contract IAPRCalculator | undefined |

### vestingLiquidityDecreasePerWeek

```solidity
function vestingLiquidityDecreasePerWeek() external view returns (uint256)
```

A fraction&#39;s numerator representing the rate at which the liquidity tokens&#39; distribution is decreased on a weekly basis




#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | uint256 | undefined |



## Events
Expand Down
Loading

0 comments on commit 2918432

Please sign in to comment.