Skip to content

Commit

Permalink
Add support for staking rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
DrZoltanFazekas committed Nov 26, 2024
1 parent 12a2c60 commit a94e825
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 18 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ forge script script/commission_Delegation.s.sol --rpc-url http://localhost:4201
using `same` for the second argument to leave the commission percentage unchanged and `true` for the third argument. Replacing the second argument with `same` and the third argument with `false` only displays the current commission rate.

## Validator Activation
If you node's account has enough ZIL for the minimum stake required, you can activate your node as a validator with a deposit of e.g. 10 million ZIL. Run
If your node's account has enough ZIL for the minimum stake required, you can activate your node as a validator with a deposit of e.g. 10 million ZIL. Run
```bash
cast send --legacy --value 10000000ether --rpc-url http://localhost:4201 --private-key $PRIVATE_KEY \
0x7a0b7e6d24ede78260c9ddbd98e828b0e11a8ea2 "deposit(bytes,bytes,bytes)" \
Expand Down Expand Up @@ -189,8 +189,13 @@ To query how much ZIL you can already claim, run
cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "getClaimable()(uint256)" --from 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --block latest --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g') ether
```

## Withdrawing Rewards
In the non-liquid variant of staking delegators can withdraw their share of the rewards earned by the validator. To query the amount of rewards available, run
## Staking and Withdrawing Rewards
In the liquid staking variant, only you as the node operator can stake the rewards accrued by the node. To do so, run
```bash
forge script script/stakeRewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 --private-key 0x...
```

In the non-liquid variant of staking, delegators can stake or withdraw their share of the rewards earned by the validator. To query the amount of rewards available, run
```bash
cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "rewards()(uint256)" --from 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --block latest --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g') ether
```
Expand All @@ -201,8 +206,14 @@ cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "rewards(uin
```
Note that `n` actually denotes the number of additional (un)stakings so that at least one is always reflected in the result, even if you specified `n = 0`.

Last but not least, to withdraw 1 ZIL of rewards using `n = 100`, run
To withdraw 1 ZIL of rewards using `n = 100`, run
```bash
forge script script/withdrawRewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, string, string)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 1000000000000000000 100 --private-key 0x...
```
with the private key of a staked account. To withdrawn as much as possible with the given value of `n` set the amount to `all`. To withdraw the chosen amount without setting `n` replace `n` with `all`. To withdraw all rewards replace both the amount and `n` with `all`.

Last but not least, in order to stake rewards instead of withdrawing them, run
```bash
forge script script/rewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, string, string)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 1000000000000000000 100 --private-key 0x...
forge script script/stakeRewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 --private-key 0x...
```
with the private key of an staked account. To withdrawn as much as possible with the given value of `n` set the amount to `all`. To withdraw the chosen amount without setting `n` replace `n` with `all`. To withdraw all rewards replace both the amount and `n` with `all`.
with the private key of a staked account.
34 changes: 34 additions & 0 deletions script/stakeRewards_Delegation.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

import {Script} from "forge-std/Script.sol";
import {BaseDelegation} from "src/BaseDelegation.sol";
import "forge-std/console.sol";

contract StakeRewards is Script {

function run(address payable proxy) external {

BaseDelegation delegation = BaseDelegation(
proxy
);

console.log("Running version: %s",
delegation.version()
);

console.log("Current stake: %s wei \r\n Current rewards: %s wei",
delegation.getStake(),
delegation.getRewards()
);

vm.broadcast();

delegation.stakeRewards();

console.log("New stake: %s wei \r\n New rewards: %s wei",
delegation.getStake(),
delegation.getRewards()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {NonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "forge-std/console.sol";

contract Rewards is Script {
contract WithdrawRewards is Script {
using Strings for string;

function run(address payable proxy, string calldata amount, string calldata additionalSteps) external {
Expand Down
2 changes: 2 additions & 0 deletions src/BaseDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ abstract contract BaseDelegation is Delegation, PausableUpgradeable, Ownable2Ste

function collectCommission() public virtual;

function stakeRewards() public virtual;

function getClaimable() public virtual view returns(uint256 total) {
BaseDelegationStorage storage $ = _getBaseDelegationStorage();
WithdrawalQueue.Fifo storage fifo = $.withdrawals[_msgSender()];
Expand Down
4 changes: 4 additions & 0 deletions src/LiquidDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ contract LiquidDelegation is BaseDelegation, ILiquidDelegation {
revert("not implemented");
}

function stakeRewards() public override {
revert("not implemented");
}

function getPrice() public view returns(uint256) {
revert("not implemented");
}
Expand Down
4 changes: 2 additions & 2 deletions src/LiquidDelegationV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,15 @@ contract LiquidDelegationV2 is BaseDelegation, ILiquidDelegation {
taxRewards();
// withdraw the unstaked deposit once the unbonding period is over
_withdrawDeposit();
$.taxedRewards -= total;
(bool success, ) = _msgSender().call{
value: total
}("");
require(success, "transfer of funds failed");
$.taxedRewards -= total;
emit Claimed(_msgSender(), total, "");
}

function stakeRewards() public onlyOwner {
function stakeRewards() public override onlyOwner {
// before the balance changes deduct the commission from the yet untaxed rewards
taxRewards();
if (address(this).balance > getTotalWithdrawals())
Expand Down
4 changes: 4 additions & 0 deletions src/NonLiquidDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ contract NonLiquidDelegation is BaseDelegation, INonLiquidDelegation {
revert("not implemented");
}

function stakeRewards() public override {
revert("not implemented");
}

function rewards() public view returns(uint256) {
revert("not implemented");
}
Expand Down
28 changes: 20 additions & 8 deletions src/NonLiquidDelegationV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ contract NonLiquidDelegationV2 is BaseDelegation, INonLiquidDelegation {
return;*/
// withdraw the unstaked deposit once the unbonding period is over
_withdrawDeposit();
$.totalRewards -= int256(total);
(bool success, ) = _msgSender().call{
value: total
}("");
$.totalRewards -= int256(total);
require(success, "transfer of funds failed");
emit Claimed(_msgSender(), total, "");
}
Expand Down Expand Up @@ -218,12 +218,12 @@ contract NonLiquidDelegationV2 is BaseDelegation, INonLiquidDelegation {
uint256 commission = untaxedRewards * getCommissionNumerator() / DENOMINATOR;
if (commission == 0)
return untaxedRewards;
$.totalRewards -= int256(commission);
// commissions are not subject to the unbonding period
(bool success, ) = owner().call{
value: commission
}("");
require(success, "transfer of commission failed");
$.totalRewards -= int256(commission);
emit CommissionPaid(owner(), commission);
return untaxedRewards - commission;
}
Expand All @@ -240,11 +240,26 @@ contract NonLiquidDelegationV2 is BaseDelegation, INonLiquidDelegation {
return withdrawRewards(amount, type(uint64).max);
}

function withdrawRewards(uint256 amount, uint64 additionalSteps) public whenNotPaused returns(uint256 taxedRewards) {
(amount, taxedRewards) = _useRewards(amount, additionalSteps);
(bool success, ) = _msgSender().call{value: amount}("");
require(success, "transfer of rewards failed");
emit RewardPaid(_msgSender(), amount);
}

function stakeRewards() public override {
(uint256 amount, ) = _useRewards(type(uint256).max, type(uint64).max);
if (_isActivated())
_increaseDeposit(amount);
_append(int256(amount));
emit Staked(_msgSender(), amount, "");
}

// if there have been more than 11,000 stakings or unstakings since the delegator's last reward
// withdrawal, calling withdrawAllRewards() would exceed the block gas limit additionalSteps is
// the number of additional stakings from which the rewards are withdrawn if zero, the rewards
// are only withdrawn from the first staking from which they have not been withdrawn yet
function withdrawRewards(uint256 amount, uint64 additionalSteps) public whenNotPaused returns(uint256) {
function _useRewards(uint256 amount, uint64 additionalSteps) internal whenNotPaused returns(uint256, uint256) {
NonLiquidDelegationStorage storage $ = _getNonLiquidDelegationStorage();
(
uint256 resultInTotal,
Expand All @@ -256,7 +271,7 @@ contract NonLiquidDelegationV2 is BaseDelegation, INonLiquidDelegation {
_rewards(additionalSteps);
// the caller has not delegated any stake
if (nextStakingIndex == 0)
return 0;
return (0, 0);
// store the rewards accrued since the last staking (`resultAfterLastStaking`)
// in order to know next time how much the caller has already withdrawn, and
// reduce the current withdrawal (`resultInTotal`) by the amount that was stored
Expand All @@ -273,10 +288,7 @@ contract NonLiquidDelegationV2 is BaseDelegation, INonLiquidDelegation {
require(amount <= $.allWithdrawnRewards[_msgSender()], "can not withdraw more than accrued");
$.allWithdrawnRewards[_msgSender()] -= amount;
$.totalRewards -= int256(amount);
(bool success, ) = _msgSender().call{value: amount}("");
require(success, "transfer of rewards failed");
emit RewardPaid(_msgSender(), amount);
return taxedRewards;
return (amount, taxedRewards);
}

function _rewards() internal view returns (
Expand Down
2 changes: 1 addition & 1 deletion rewards.sh → withdrawRewards.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if [[ "$variant" != "INonLiquidDelegation" ]]; then
exit 1
fi

forge script script/rewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, string, string)" $1 $amount $steps --private-key $2
forge script script/withdrawRewards_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, string, string)" $1 $amount $steps --private-key $2

block=$(cast rpc eth_blockNumber --rpc-url http://localhost:4201)
block_num=$(echo $block | tr -d '"' | cast to-dec --base-in 16)
Expand Down

0 comments on commit a94e825

Please sign in to comment.