Skip to content

Commit

Permalink
Fix share calculation and increase / decrease deposit on staking / un…
Browse files Browse the repository at this point in the history
…staking (#3)

* removed logging

* removed logging

* implemented commission

* Implement getCommission() and extend readme

* Implement getCommissionAddress()

* Introduce script params

* Implement claiming after unbonding period

* Implement deposit increase and decrease

* Modify order of staking and unstaking steps

* Create bash scripts for staking, unstaking, claiming tests

* Improve bash and foundry scripts

* Fix price calculation and commission formatting

* Fix bc scale in bash scripts

* Fix tests, add getPrice() and getClaimable()

* Improve staking script
  • Loading branch information
DrZoltanFazekas authored Oct 24, 2024
1 parent 5bcd747 commit f0cfcb6
Show file tree
Hide file tree
Showing 19 changed files with 1,792 additions and 371 deletions.
118 changes: 75 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ forge install OpenZeppelin/openzeppelin-contracts --no-commit
```

## Contract Deployment
The delegation contract is used by delegators to stake and unstake ZIL with the respective validator. It acts as the validator node's control address and interacts with the `Deposit` system contract. `DelegationV1` is the initial implementation of the delegation contract is upgradeable: `DelegationV2` deploys a `NonRebasingLST` contract when it is initialized and `DelegationV3` adds the newest features.
The delegation contract is used by delegators to stake and unstake ZIL with the respective validator. It acts as the validator node's control address and interacts with the `Deposit` system contract. `Delegation` is the initial implementation of the delegation contract that creates a `NonRebasingLST` contract when it is initialized. `DelegationV2` implements staking, unstaking and other features.

The delegation contract shall be deployed and upgraded by the account with the private key that was used to run the validator node and was used to generate its BLS keypair and peer id. Make sure the `PRIVATE_KEY` environment variable is set accordingly.

To deploy `DelegationV1` run
```
forge script script/deploy_Delegation.s.sol --rpc-url https://api.zq2-devnet.zilliqa.com --broadcast --legacy
To deploy `Delegation` run
```bash
forge script script/deploy_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy
```
You will see an output like this:
```
Expand All @@ -29,8 +29,8 @@ You will see an output like this:
You will need the proxy address from the above output in all commands below.

To upgrade the contract to `DelegationV2`, run
```
forge script script/upgrade_Delegation.s.sol --rpc-url https://api.zq2-devnet.zilliqa.com --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2
```bash
forge script script/upgrade_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2
```

The output will look like this:
Expand All @@ -42,79 +42,111 @@ The output will look like this:
Upgraded to version: 2
```

To upgrade the contract to `DelegationV3`, replace line 33 in `upgrade_Delegation.s.sol` with
```solidity
new DelegationV3()
## Contract Configuration

Now or at a later time you can set the commission on the rewards the validator earns to e.g. 10% as follows:
```bash
forge script script/commission_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, uint16)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 1000
```
and run

The output will contain the following information:
```
forge script script/upgrade_Delegation.s.sol --rpc-url https://api.zq2-devnet.zilliqa.com --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2
Running version: 2
LST address: 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83
Old commission rate: 0.0%
New commission rate: 10.0%
```
again.

The output will look like this:
```
Signer is 0x15fc323DFE5D5DCfbeEdc25CEcbf57f676634d77
Upgrading from version: 2
Owner is 0x15fc323DFE5D5DCfbeEdc25CEcbf57f676634d77
New implementation deployed: 0x90A65311b6C7246FFD1F212C123cfE351a6d65A9
Upgraded to version: 3
Note that the commission rate is specified as an integer to be devided by the `DENOMINATOR` which can be retrieved from the delegation contract:
```bash
cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "DENOMINATOR()(uint256)" --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g'
```

## Validator Activation
Now you are ready to use the contract to activate your node as a validator with a deposit of e.g. 10 million ZIL. Run
```
cast send --legacy --value 10000000ether --rpc-url https://api.zq2-devnet.zilliqa.com --private-key $PRIVATE_KEY \
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
```bash
cast send --legacy --value 10000000ether --rpc-url http://localhost:4201 --private-key $PRIVATE_KEY \
0x7a0b7e6d24ede78260c9ddbd98e828b0e11a8ea2 "deposit(bytes,bytes,bytes)" \
0x92fbe50544dce63cfdcc88301d7412f0edea024c91ae5d6a04c7cd3819edfc1b9d75d9121080af12e00f054d221f876c \
0x002408011220d5ed74b09dcbe84d3b32a56c01ab721cf82809848b6604535212a219d35c412f \
0xb14832a866a49ddf8a3104f8ee379d29c136f29aeb8fccec9d7fb17180b99e8ed29bee2ada5ce390cb704bc6fd7f5ce814f914498376c4b8bc14841a57ae22279769ec8614e2673ba7f36edc5a4bf5733aa9d70af626279ee2b2cde939b4bd8a
```
with the BLS public key, the peer id and the BLS signature of your node. Note that the peer id must be converted from base58 to hex.

Make sure your node's account has the 10 million ZIL and your node is fully synced before you run the above command.
with the BLS public key, the peer id and the BLS signature of your node. Note that the peer id must be converted from base58 to hex. Make sure your node is fully synced before you run the above command.

Note that the reward address registered for your validator node will be the address of the delegation contract (the proxy contract to be more precise).

Alternatively, you can proceed to the next section and delegate stake until the contract's balance reaches the 10 million ZIL minimum stake required for the activation, and then run
```bash
cast send --legacy --rpc-url http://localhost:4201 --private-key $PRIVATE_KEY \
0x7a0b7e6d24ede78260c9ddbd98e828b0e11a8ea2 "deposit2(bytes,bytes,bytes)" \
0x92fbe50544dce63cfdcc88301d7412f0edea024c91ae5d6a04c7cd3819edfc1b9d75d9121080af12e00f054d221f876c \
0x002408011220d5ed74b09dcbe84d3b32a56c01ab721cf82809848b6604535212a219d35c412f \
0xb14832a866a49ddf8a3104f8ee379d29c136f29aeb8fccec9d7fb17180b99e8ed29bee2ada5ce390cb704bc6fd7f5ce814f914498376c4b8bc14841a57ae22279769ec8614e2673ba7f36edc5a4bf5733aa9d70af626279ee2b2cde939b4bd8a
```
to deposit all of it.

## Staking and Unstaking
If the above transaction was successful and the node became a validator, it can accept delegations. In order to stake e.g. 200 ZIL, run
```
forge script script/stake_Delegation.s.sol --rpc-url https://api.zq2-devnet.zilliqa.com --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 --private-key 0x...
```bash
forge script script/stake_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, uint256)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 200000000000000000000 --private-key 0x...
```
with the private key of the delegator account. Make sure the account's balance can cover the transaction fees plus the 200 ZIL to be delegated.

The output will look like this:
```
Running version: 3
Current stake: 10000000000000000000000000
Current rewards: 110314207650273223687
Running version: 2
Current stake: 10000000000000000000000000 wei
Current rewards: 110314207650273223687 wei
LST address: 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83
Owner balance: 10000000000000000000000000
Staker balance: 0
Staker balance: 199993793908430833324
Owner balance: 10000000000000000000000000 LST
Staker balance before: 99899145245801454561224 wei 0 LST
Staker balance after: 99699145245801454561224 wei 199993793908430833324 LST
```

Note that the staker LST balance in the output will be different from the actual LST balance which you can query by running
```
cast call 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83 "balanceOf(address)(uint256)" 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --rpc-url https://api.zq2-devnet.zilliqa.com
```bash
cast call 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83 "balanceOf(address)(uint256)" 0xd819fFcE7A58b1E835c25617Db7b46a00888B013 --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g'
```
This is due to the fact that the above output was generated based on the local script execution before the transaction got submitted to the network.

You can copy the LST address from the above output and add it to your wallet to transfer your liquid staking tokens to another account if you want to.

Last but not least, to unstake, run
To query the current price of an LST, run
```bash
cast to-unit $(cast call 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 "getPrice()(uint256)" --block latest --rpc-url http://localhost:4201 | sed 's/\[[^]]*\]//g') ether
```
forge script script/unstake_Delegation.s.sol --rpc-url https://api.zq2-devnet.zilliqa.com --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 --private-key 0x...

To unstake e.g. 100 LST, run
```bash
forge script script/unstake_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable, uint256)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 100000000000000000000 --private-key 0x...
```
with the private key of an account that holds some LST.

The output will look like this:
```
Running version: 3
Current stake: 10000000000000000000000000
Current rewards: 331912568306010928520
Running version: 2
Current stake: 10000000000000000000000000 wei
Current rewards: 331912568306010928520 wei
LST address: 0x9e5c257D1c6dF74EaA54e58CdccaCb924669dc83
Owner balance: 10000000000000000000000000
Staker balance: 199993784619390291653
Staker balance: 0
Owner balance: 10000000000000000000000000 LST
Staker balance before: 99698814298179759361224 wei 199993784619390291653 LST
Staker balance after: 99698814298179759361224 wei 99993784619390291653 LST
```

Last but not least, to claim the amount that is available after the unbonding period, run
```bash
forge script script/claim_Delegation.s.sol --rpc-url http://localhost:4201 --broadcast --legacy --sig "run(address payable)" 0x7A0b7e6D24eDe78260c9ddBD98e828B0e11A8EA2 --private-key 0x...
```
with the private key of an account that unstaked some LST.

The output will look like this:
```
Running version: 2
Staker balance before: 99698086421983460161224 wei
Staker balance after: 99798095485861371162343 wei
```

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
```
41 changes: 41 additions & 0 deletions claim.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

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

staker=$(cast wallet address $2)

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')

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")
if [[ "$tmp" != "" ]]; then
tmp=${tmp#*: }
tmp=$(cast abi-decode --input "x(uint256)" $tmp | sed 's/\[[^]]*\]//g')
tmp=(${tmp})
d1=${tmp[0]}
#d1=$(echo $tmp | sed -n -e 1p | sed 's/\[[^]]*\]//g')
fi

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

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')

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
echo $(date +"%T,%3N") $block_num
33 changes: 33 additions & 0 deletions script/claim_Delegation.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

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

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

address staker = msg.sender;

DelegationV2 delegation = DelegationV2(
proxy
);

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

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

vm.broadcast();

delegation.claim();

console.log("Staker balance after: %s wei",
staker.balance
);
}
}
42 changes: 42 additions & 0 deletions script/commission_Delegation.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

import {Script} from "forge-std/Script.sol";
import {NonRebasingLST} from "src/NonRebasingLST.sol";
import {DelegationV2} from "src/DelegationV2.sol";
import {Console} from "src/Console.sol";
import "forge-std/console.sol";

contract Stake is Script {
function run(address payable proxy, uint16 commissionNumerator) external {

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

DelegationV2 delegation = DelegationV2(
proxy
);

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

NonRebasingLST lst = NonRebasingLST(delegation.getLST());
console.log("LST address: %s",
address(lst)
);

Console.log("Old commission rate: %s.%s%s%%",
delegation.getCommissionNumerator(),
2
);

vm.broadcast(deployerPrivateKey);

delegation.setCommissionNumerator(commissionNumerator);

Console.log("New commission rate: %s.%s%s%%",
delegation.getCommissionNumerator(),
2
);
}
}
6 changes: 0 additions & 6 deletions script/deploy_Delegation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ contract Deploy is Script {
vm.startBroadcast(deployerPrivateKey);

address implementation = address(
//new Delegation{salt: "zilliqa"}()
new Delegation()
);

Expand All @@ -25,7 +24,6 @@ contract Deploy is Script {
);

address payable proxy = payable(
//new ERC1967Proxy{salt: "zilliqa"}(implementation, initializerCall)
new ERC1967Proxy(implementation, initializerCall)
);

Expand All @@ -39,10 +37,6 @@ contract Deploy is Script {
proxy
);

delegation.stake();
delegation.unstake();
delegation.claim();

console.log("Deployed version: %s",
delegation.version()
);
Expand Down
Loading

0 comments on commit f0cfcb6

Please sign in to comment.