Skip to content

Commit

Permalink
Fix compatibility with deposit contract
Browse files Browse the repository at this point in the history
  • Loading branch information
DrZoltanFazekas committed Nov 11, 2024
1 parent 0706b1c commit ca28b81
Show file tree
Hide file tree
Showing 19 changed files with 520 additions and 246 deletions.
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ You will see an output like this:
Owner is 0x15fc323DFE5D5DCfbeEdc25CEcbf57f676634d77
```

You will need the proxy address from the above output in all commands below.
You will need the proxy address from the above output in all commands below. If you have the address of a proxy contract but don't know which variant of staking it supports, run
```bash
forge script script/variant_Delegation.s.sol --rpc-url http://localhost:4201 --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2
```
The output will be `ILiquidStaking`, `INonLiquidStaking` or none of them if the address is not a valid delegation contract.

To upgrade the contract to `LiquidDelegationV2`, run
To upgrade the contract to `LiquidDelegationV2` or `NonLiquidDelegationV2` depending on the staking model it implements, run
```bash
forge script script/upgrade_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2
```
Expand Down Expand Up @@ -73,9 +77,9 @@ cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "DENOMINATOR()(uint256)" --

Once the validator is activated and starts earning rewards, commissions are transferred automatically to the validator node's account. Commissions of a non-liquid staking validator are deducted when delegators withdraw rewards. In case of the liquid staking variant, commissions are deducted each time delegators stake, unstake or claim what they unstaked, or when the node requests the outstanding commissions that haven't been transferred yet. To collect them, run
```bash
forge script script/commission_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, uint16, bool)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 10001 true
forge script script/commission_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, string, bool)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 10001 true
```
using any value for the second argument that is larger than the `DENOMINATOR` to leave the commission percentage unchanged and `true` for the third argument.
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
Expand Down Expand Up @@ -115,7 +119,6 @@ The output will look like this for liquid staking:
Current stake: 10000000000000000000000000 wei
Current rewards: 110314207650273223687 wei
LST address: 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83
Owner balance: 10000000000000000000000000 LST
Staker balance before: 99899145245801454561224 wei 0 LST
Staker balance after: 99699145245801454561224 wei 199993793908430833324 LST
```
Expand Down Expand Up @@ -152,7 +155,6 @@ The output will look like this for liquid staking:
Current stake: 10000000000000000000000000 wei
Current rewards: 331912568306010928520 wei
LST address: 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83
Owner balance: 10000000000000000000000000 LST
Staker balance before: 99698814298179759361224 wei 199993784619390291653 LST
Staker balance after: 99698814298179759361224 wei 99993784619390291653 LST
```
Expand Down Expand Up @@ -181,4 +183,22 @@ The output will look like this:
To query how much ZIL you can already claim, run
```bash
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
```bash
cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "rewards()(uint256)" --from 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --block latest --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g') ether
```

In case you haven't withdrawn rewards for a long time during which many delegators staked or unstaked, the gas used by the above function might hit the block limit. In this case you can withdraw rewards from the period between the (un)staking until which you withdrew rewards last time and the `n`th subsequent (un)staking. Choose a number `0 <= n <= 11000` e.g. `100` and run
```bash
cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "rewards(uint64)(uint256)" 100 --from 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --block latest --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g') ether
```
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
```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...
```
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`.
30 changes: 23 additions & 7 deletions claim.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
#!/bin/bash

if [ $# -ne 2 ]; then
echo "Provide the delegation contract address and a staker private key as arguments."
exit 1
echo "Provide the delegation contract address and a staker private key as arguments."
exit 1
fi

staker=$(cast wallet address $2)

temp=$(forge script script/variant_Delegation.s.sol --rpc-url http://localhost:4201 --sig "run(address payable)" $1 | tail -n 1)
variant=$(sed -E 's/\s\s([a-zA-Z0-9]+)/\1/' <<< "$temp")
if [[ "$variant" == "$temp" ]]; then
echo Incompatible delegation contract at $1
exit 1
fi

forge script script/claim_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" $1 --private-key $2 -vvvv

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

echo rewardsAfterClaiming = $(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
echo taxedRewardsAfterClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
if [[ "$variant" == "ILiquidDelegation" ]]; then
echo taxedRewardsAfterClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
fi

staker_wei_after=$(cast rpc eth_getBalance $staker $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)

tmp=$(cast logs --from-block $block_num --to-block $block_num --address $1 "Claimed(address,uint256)" --rpc-url http://localhost:4201 | grep "data")
tmp=$(cast logs --from-block $block_num --to-block $block_num --address $1 "Claimed(address,uint256,bytes)" --rpc-url http://localhost:4201 | grep "data")
if [[ "$tmp" != "" ]]; then
tmp=${tmp#*: }
tmp=$(cast abi-decode --input "x(uint256)" $tmp | sed 's/\[[^]]*\]//g')
tmp=$(cast abi-decode --input "x(uint256,bytes)" $tmp | sed 's/\[[^]]*\]//g')
tmp=(${tmp})
d1=${tmp[0]}
d2=${tmp[1]}
#d1=$(echo $tmp | sed -n -e 1p | sed 's/\[[^]]*\]//g')
#d2=$(echo $tmp | sed -n -e 2p | sed 's/\[[^]]*\]//g')

fi

echo $(date +"%T,%3N") $block_num
Expand All @@ -32,10 +44,14 @@ block_num=$((block_num-1))
block=$(echo $block_num | cast to-hex --base-in 10)

echo rewardsBeforeClaiming = $(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
echo taxedRewardsBeforeClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
if [[ "$variant" == "ILiquidDelegation" ]]; then
echo taxedRewardsBeforeClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
fi

staker_wei_before=$(cast rpc eth_getBalance $staker $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)

echo claimed amount - gas fee = $(bc -l <<< "scale=18; $staker_wei_after-$staker_wei_before") wei
if [[ "$tmp" != "" ]]; then echo event Claimed\($staker, $d1\) emitted; fi

if [[ "$tmp" != "" ]]; then echo event Claimed\($staker, $d1, $d2\) emitted; fi

echo $(date +"%T,%3N") $block_num
94 changes: 94 additions & 0 deletions rewards.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash

if [ $# -lt 2 ]; then
echo "Provide the delegation contract address, a staker private key and optionally an amount and number of steps as arguments."
exit 1
fi

if [ $# -eq 3 ]; then
amount="$3"
else
amount="all"
fi

if [ $# -eq 4 ]; then
steps="$4"
else
steps="all"
fi

staker=$(cast wallet address $2)

temp=$(forge script script/variant_Delegation.s.sol --rpc-url http://localhost:4201 --sig "run(address payable)" $1 | tail -n 1)
variant=$(sed -E 's/\s\s([a-zA-Z0-9]+)/\1/' <<< "$temp")
if [[ "$variant" == "$temp" ]]; then
echo Incompatible delegation contract at $1
exit 1
fi
if [[ "$variant" != "INonLiquidDelegation" ]]; then
echo Reward withdrawal not supported by $1
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

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

owner=$(cast call $1 "owner()(address)" --block $block_num --rpc-url http://localhost:4201)

rewardsAfterWithdrawal=$(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
echo rewardsAfterWithdrawal = $rewardsAfterWithdrawal

staker_wei_after=$(cast rpc eth_getBalance $staker $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)
owner_wei_after=$(cast rpc eth_getBalance $owner $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)

tmp1=$(cast logs --from-block $block_num --to-block $block_num --address $1 "RewardPaid(address,uint256)" --rpc-url http://localhost:4201 | grep "data")
if [[ "$tmp1" != "" ]]; then
tmp1=${tmp1#*: }
tmp1=$(cast abi-decode --input "x(uint256)" $tmp1 | sed 's/\[[^]]*\]//g')
tmp1=(${tmp1})
d1=${tmp1[0]}
#d1=$(echo $tmp | sed -n -e 1p | sed 's/\[[^]]*\]//g')
fi

tmp2=$(cast logs --from-block $block_num --to-block $block_num --address $1 "CommissionPaid(address,uint256)" --rpc-url http://localhost:4201 | grep "data")
if [[ "$tmp2" != "" ]]; then
tmp2=${tmp2#*: }
tmp2=$(cast abi-decode --input "x(uint256)" $tmp2 | sed 's/\[[^]]*\]//g')
tmp2=(${tmp2})
d2=${tmp2[0]}
#d2=$(echo $tmp2 | sed -n -e 1p | sed 's/\[[^]]*\]//g')
fi

x=$(cast call $1 "rewards()(uint256)" --from $staker --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
staker_rewards_after_withdrawal=$(cast to-unit $x ether)

echo $(date +"%T,%3N") $block_num

block_num=$((block_num-1))
block=$(echo $block_num | cast to-hex --base-in 10)

rewardsBeforeWithdrawal=$(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
echo rewardsBeforeWithdrawal = $rewardsBeforeWithdrawal

stake=$(cast call $1 "getStake()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
commissionNumerator=$(cast call $1 "getCommissionNumerator()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
denominator=$(cast call $1 "DENOMINATOR()(uint256)" --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')

staker_wei_before=$(cast rpc eth_getBalance $staker $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)
owner_wei_before=$(cast rpc eth_getBalance $owner $block --rpc-url http://localhost:4201 | tr -d '"' | cast to-dec --base-in 16)

x=$(cast call $1 "rewards()(uint256)" --from $staker --block $block_num --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g')
staker_rewards_before_withdrawal=$(cast to-unit $x ether)

echo staker rewards before withdrawal: $staker_rewards_before_withdrawal ZIL
echo staker rewards after withdrawal: $staker_rewards_after_withdrawal ZIL
echo withdrawn rewards - gas fee = $(bc -l <<< "scale=18; $staker_wei_after-$staker_wei_before") wei
echo validator commission = $(bc -l <<< "scale=18; $owner_wei_after-$owner_wei_before") wei
echo total reward reduction = $(bc -l <<< "scale=18; $rewardsBeforeWithdrawal-$rewardsAfterWithdrawal") wei

if [[ "$tmp1" != "" ]]; then echo event RewardPaid\($staker, $d1\) emitted; fi
if [[ "$tmp2" != "" ]]; then echo event CommissionPaid\($owner, $d2\) emitted; fi

echo $(date +"%T,%3N") $block_num
1 change: 0 additions & 1 deletion script/claim_Delegation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import "forge-std/console.sol";

contract Claim is Script {
function run(address payable proxy) external {

address staker = msg.sender;

BaseDelegation delegation = BaseDelegation(
Expand Down
10 changes: 6 additions & 4 deletions script/commission_Delegation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pragma solidity ^0.8.26;
import {Script} from "forge-std/Script.sol";
import {BaseDelegation} from "src/BaseDelegation.sol";
import {Console} from "src/Console.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "forge-std/console.sol";

contract Stake is Script {
function run(address payable proxy, uint16 commissionNumerator, bool collectCommission) external {
contract Commission is Script {
using Strings for string;

function run(address payable proxy, string calldata commissionNumerator, bool collectCommission) external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

BaseDelegation delegation = BaseDelegation(
Expand All @@ -24,10 +26,10 @@ contract Stake is Script {
2
);

if (commissionNumerator <= delegation.DENOMINATOR()) {
if (!commissionNumerator.equal("same")) {
vm.broadcast(deployerPrivateKey);

delegation.setCommissionNumerator(commissionNumerator);
delegation.setCommissionNumerator(uint16(vm.parseUint(commissionNumerator)));

Console.log("New commission rate: %s.%s%s%%",
delegation.getCommissionNumerator(),
Expand Down
45 changes: 45 additions & 0 deletions script/rewards_Delegation.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

import {Script} from "forge-std/Script.sol";
import {NonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "forge-std/console.sol";

contract Rewards is Script {
using Strings for string;

function run(address payable proxy, string calldata amount, string calldata additionalSteps) external {
address staker = msg.sender;

NonLiquidDelegation delegation = NonLiquidDelegation(
proxy
);

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

console.log("Staker balance before: %s wei",
staker.balance
);

vm.broadcast();

//TODO: figure out why Strings.parseUint() is not found
if (amount.equal("all"))
if (additionalSteps.equal("all"))
delegation.withdrawAllRewards();
else
delegation.withdrawAllRewards(uint64(vm.parseUint(additionalSteps)));
else
if (additionalSteps.equal("all"))
delegation.withdrawRewards(vm.parseUint(amount));
else
delegation.withdrawRewards(vm.parseUint(amount), uint64(vm.parseUint(additionalSteps)));

console.log("Staker balance after: %s wei",
staker.balance
);
}
}
7 changes: 0 additions & 7 deletions script/stake_Delegation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ contract Stake is Script {
using ERC165Checker for address;

function run(address payable proxy, uint256 amount) external {

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address owner = vm.addr(deployerPrivateKey);
address staker = msg.sender;

BaseDelegation delegation = BaseDelegation(
Expand All @@ -37,10 +34,6 @@ contract Stake is Script {
address(lst)
);

console.log("Owner balance: %s LST",
lst.balanceOf(owner)
);

console.log("Staker balance before: %s wei %s LST",
staker.balance,
lst.balanceOf(staker)
Expand Down
22 changes: 10 additions & 12 deletions script/unstake_Delegation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ import {Script} from "forge-std/Script.sol";
import {NonRebasingLST} from "src/NonRebasingLST.sol";
import {BaseDelegation} from "src/BaseDelegation.sol";
import {ILiquidDelegation} from "src/LiquidDelegation.sol";
import {LiquidDelegationV2} from "src/LiquidDelegationV2.sol";
import {INonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "forge-std/console.sol";

contract Unstake is Script {
using ERC165Checker for address;

function run(address payable proxy, uint256 amount) external {

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address owner = vm.addr(deployerPrivateKey);
address staker = msg.sender;

BaseDelegation delegation = BaseDelegation(
Expand All @@ -32,15 +29,11 @@ contract Unstake is Script {
);

if (address(delegation).supportsInterface(type(ILiquidDelegation).interfaceId)) {
NonRebasingLST lst = NonRebasingLST(LiquidDelegationV2(payable(address(delegation))).getLST());
NonRebasingLST lst = NonRebasingLST(ILiquidDelegation(payable(address(delegation))).getLST());
console.log("LST address: %s",
address(lst)
);

console.log("Owner balance: %s LST",
lst.balanceOf(owner)
);

console.log("Staker balance before: %s wei %s LST",
staker.balance,
lst.balanceOf(staker)
Expand All @@ -50,11 +43,16 @@ contract Unstake is Script {
amount = lst.balanceOf(staker);
}

} else {
} else if (address(delegation).supportsInterface(type(INonLiquidDelegation).interfaceId)) {
console.log("Staker balance before: %s wei",
staker.balance
);
}

if (amount == 0) {
amount = INonLiquidDelegation(address(delegation)).getDelegatedStake();
}
} else
return;

vm.broadcast();

Expand All @@ -63,7 +61,7 @@ contract Unstake is Script {
);

if (address(delegation).supportsInterface(type(ILiquidDelegation).interfaceId)) {
NonRebasingLST lst = NonRebasingLST(LiquidDelegationV2(payable(address(delegation))).getLST());
NonRebasingLST lst = NonRebasingLST(ILiquidDelegation(payable(address(delegation))).getLST());
console.log("Staker balance after: %s wei %s LST",
staker.balance,
lst.balanceOf(staker)
Expand Down
Loading

0 comments on commit ca28b81

Please sign in to comment.