Skip to content

Commit

Permalink
Apply guidelines, improve tests and docs, support solo staker migrati…
Browse files Browse the repository at this point in the history
…on (#24)

* Extend readme

* Extend readme, fix RewardPaid event topic

* Allow customizing LST name and symbol

* Assert owner is not one of the stakers used in the tests

* Display the liquid staking token symbol in the scripts

* Mention the bash scripts in the readme

* Replace occurances of localhost url from bash scripts

* Add assertions to test reward calculation

* Implement functions requested by staking portal

* Implement migration of solo stakers to staking pools

* Apply linter changes, rename forge scripts

* Ensure compatibility with deposit v3

* Restructure the readme, add more solhint rules

* Get rid of compiler warnings

* Minor fixes requested by the reviewer
  • Loading branch information
DrZoltanFazekas authored Dec 18, 2024
1 parent a23f9e4 commit 7ca62a5
Show file tree
Hide file tree
Showing 30 changed files with 1,189 additions and 640 deletions.
26 changes: 26 additions & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"extends": "solhint:recommended",
"plugins": [],
"rules": {
"no-inline-assembly": "off",
"avoid-low-level-calls": "off",
"reason-string": ["warn", {"maxLength": 70}],
"func-visibility": ["warn", {"ignoreConstructors": true}],
"gas-custom-errors": "off",
"no-empty-blocks": "off",
"no-complex-fallback": "off",
"interface-starts-with-i": "warn",
"func-param-name-mixedcase": "warn",
"modifier-name-mixedcase": "warn",
"gas-calldata-parameters": "warn",
"gas-indexed-events": "warn",
"gas-length-in-loops": "warn",
"comprehensive-interface": "warn",
"gas-increment-by-one": "off",
"private-vars-leading-underscore": ["off", {"strict": false}],
"named-parameters-mapping": "off",
"foundry-test-functions": ["off"],
"imports-order": "off",
"max-line-length": ["off", 120]
}
}
183 changes: 144 additions & 39 deletions README.md

Large diffs are not rendered by default.

22 changes: 12 additions & 10 deletions claim.sh
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
#!/bin/bash

url=http://localhost:4201

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)

temp=$(forge script script/variant_Delegation.s.sol --rpc-url http://localhost:4201 --sig "run(address payable)" $1 | tail -n 1)
temp=$(forge script script/CheckVariant.s.sol --rpc-url $url --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
forge script script/Claim.s.sol --rpc-url $url --broadcast --legacy --sig "run(address payable)" $1 --private-key $2 -vvvv

block=$(cast rpc eth_blockNumber --rpc-url http://localhost:4201)
block=$(cast rpc eth_blockNumber --rpc-url $url)
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 rewardsAfterClaiming = $(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url $url | 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')
echo taxedRewardsAfterClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url $url | sed 's/\[[^]]*\]//g')
fi

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

tmp=$(cast logs --from-block $block_num --to-block $block_num --address $1 "Claimed(address,uint256,bytes)" --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 $url | grep "data")
if [[ "$tmp" != "" ]]; then
tmp=${tmp#*: }
tmp=$(cast abi-decode --input "x(uint256,bytes)" $tmp | sed 's/\[[^]]*\]//g')
Expand All @@ -43,12 +45,12 @@ 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 rewardsBeforeClaiming = $(cast call $1 "getRewards()(uint256)" --block $block_num --rpc-url $url | 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')
echo taxedRewardsBeforeClaiming = $(cast call $1 "getTaxedRewards()(uint256)" --block $block_num --rpc-url $url | sed 's/\[[^]]*\]//g')
fi

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

echo claimed amount - gas fee = $(bc -l <<< "scale=18; $stakerWeiAfter-$stakerWeiBefore") wei

Expand Down
7 changes: 4 additions & 3 deletions script/variant_Delegation.s.sol → script/CheckVariant.s.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
import {Script} from "forge-std/Script.sol";
import {ILiquidDelegation} from "src/LiquidDelegation.sol";
import {INonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "forge-std/console.sol";
import {console} from "forge-std/console.sol";

contract Upgrade is Script {
contract CheckVariant is Script {
using ERC165Checker for address;

function run(address proxy) external {
function run(address proxy) external view {

if (proxy.supportsInterface(type(ILiquidDelegation).interfaceId))
console.log("ILiquidDelegation");
Expand Down
3 changes: 2 additions & 1 deletion script/claim_Delegation.s.sol → script/Claim.s.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

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

contract Claim is Script {
function run(address payable proxy) external {
Expand Down
27 changes: 17 additions & 10 deletions script/deploy_Delegation.s.sol → script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
import {Script} from "forge-std/Script.sol";
import {BaseDelegation} from "src/BaseDelegation.sol";
import {LiquidDelegation} from "src/LiquidDelegation.sol";
import {NonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "forge-std/console.sol";
import {console} from "forge-std/console.sol";

contract Deploy is Script {
using Strings for string;

function run(string calldata variant) external {
function run(string calldata variant, string calldata name, string calldata symbol) external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address owner = vm.addr(deployerPrivateKey);
console.log("Signer is %s", owner);

vm.startBroadcast(deployerPrivateKey);

address implementation;
bytes memory initializerCall;

if (variant.equal("LiquidDelegation"))
if (variant.equal("LiquidDelegation")) {
implementation = address(new LiquidDelegation());
else if (variant.equal("NonLiquidDelegation"))
initializerCall = abi.encodeWithSignature(
"initialize(address,string,string)",
owner,
name,
symbol
);
} else if (variant.equal("NonLiquidDelegation")) {
implementation = address(new NonLiquidDelegation());
else
initializerCall = abi.encodeWithSignature(
"initialize(address)",
owner
);
} else
return;

bytes memory initializerCall = abi.encodeWithSignature(
"initialize(address)",
owner
);

address payable proxy = payable(
new ERC1967Proxy(implementation, initializerCall)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
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";
import {console} from "forge-std/console.sol";

contract Commission is Script {
contract ManageCommission is Script {
using Strings for string;

function run(address payable proxy, string calldata commissionNumerator, bool collectCommission) external {
Expand Down
13 changes: 8 additions & 5 deletions script/stake_Delegation.s.sol → script/Stake.s.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
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 {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "forge-std/console.sol";
import {console} from "forge-std/console.sol";

contract Stake is Script {
using ERC165Checker for address;
Expand All @@ -34,9 +35,10 @@ contract Stake is Script {
address(lst)
);

console.log("Staker balance before: %s wei %s LST",
console.log("Staker balance before: %s wei %s %s",
staker.balance,
lst.balanceOf(staker)
lst.balanceOf(staker),
lst.symbol()
);
} else {
console.log("Staker balance before: %s wei",
Expand All @@ -52,9 +54,10 @@ contract Stake is Script {

if (address(delegation).supportsInterface(type(ILiquidDelegation).interfaceId)) {
NonRebasingLST lst = NonRebasingLST(LiquidDelegationV2(payable(address(delegation))).getLST());
console.log("Staker balance after: %s wei %s LST",
console.log("Staker balance after: %s wei %s %s",
staker.balance,
lst.balanceOf(staker)
lst.balanceOf(staker),
lst.symbol()
);
} else {
console.log("Staker balance after: %s wei",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

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

contract StakeRewards is Script {

Expand Down
13 changes: 8 additions & 5 deletions script/unstake_Delegation.s.sol → script/Unstake.s.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
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 {INonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "forge-std/console.sol";
import {console} from "forge-std/console.sol";

contract Unstake is Script {
using ERC165Checker for address;
Expand All @@ -34,9 +35,10 @@ contract Unstake is Script {
address(lst)
);

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

if (amount == 0) {
Expand All @@ -63,9 +65,10 @@ contract Unstake is Script {

if (address(delegation).supportsInterface(type(ILiquidDelegation).interfaceId)) {
NonRebasingLST lst = NonRebasingLST(ILiquidDelegation(payable(address(delegation))).getLST());
console.log("Staker balance after: %s wei %s LST",
console.log("Staker balance after: %s wei %s %s",
staker.balance,
lst.balanceOf(staker)
lst.balanceOf(staker),
lst.symbol()
);
} else {
console.log("Staker balance after: %s wei",
Expand Down
3 changes: 2 additions & 1 deletion script/upgrade_Delegation.s.sol → script/Upgrade.s.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
import {Script} from "forge-std/Script.sol";
import {BaseDelegation} from "src/BaseDelegation.sol";
import {ILiquidDelegation} from "src/LiquidDelegation.sol";
import {INonLiquidDelegation} from "src/NonLiquidDelegation.sol";
import {LiquidDelegationV2} from "src/LiquidDelegationV2.sol";
import {NonLiquidDelegationV2} from "src/NonLiquidDelegationV2.sol";
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "forge-std/console.sol";
import {console} from "forge-std/console.sol";

contract Upgrade is Script {
using ERC165Checker for address;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/* solhint-disable no-console */
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";
import {console} from "forge-std/console.sol";

contract WithdrawRewards is Script {
using Strings for string;
Expand Down
Loading

0 comments on commit 7ca62a5

Please sign in to comment.