From b338668ea5d438a36fd56bc0a2488ead9e2307e6 Mon Sep 17 00:00:00 2001 From: Dmitry Gusakov Date: Tue, 14 Jan 2025 10:32:18 +0100 Subject: [PATCH] feat: Use REQUEST_BURN_MY_STETH_ROLE role (#381) --- Justfile | 1 + script/fork-helpers/SimulateVote.s.sol | 13 ++++++++++++- src/abstract/CSBondCore.sol | 10 ++++------ src/interfaces/IBurner.sol | 9 +++++---- test/CSAccounting.t.sol | 8 ++++---- test/CSBondCore.t.sol | 19 ++++++------------- test/fork/integration/Penalty.t.sol | 4 ++-- test/fork/voting/StatePostVote.t.sol | 6 ++++++ test/helpers/Fixtures.sol | 3 +++ test/helpers/mocks/BurnerMock.sol | 13 +++---------- 10 files changed, 46 insertions(+), 40 deletions(-) diff --git a/Justfile b/Justfile index a86f5ea8..15bf1437 100644 --- a/Justfile +++ b/Justfile @@ -195,6 +195,7 @@ test-local *args: @while ! echo exit | nc {{anvil_host}} {{anvil_port}} > /dev/null; do sleep 1; done DEPLOYER_PRIVATE_KEY=`cat localhost.json | jq -r ".private_keys[0]"` \ just deploy --silent + DEPLOY_CONFIG=./artifacts/local/deploy-{{chain}}.json \ RPC_URL={{anvil_rpc_url}} \ just test-deployment {{args}} diff --git a/script/fork-helpers/SimulateVote.s.sol b/script/fork-helpers/SimulateVote.s.sol index 01e250dc..823ff94b 100644 --- a/script/fork-helpers/SimulateVote.s.sol +++ b/script/fork-helpers/SimulateVote.s.sol @@ -11,6 +11,7 @@ import { CSModule } from "../../src/CSModule.sol"; import { CSAccounting } from "../../src/CSAccounting.sol"; import { CSFeeOracle } from "../../src/CSFeeOracle.sol"; import { IBurner } from "../../src/interfaces/IBurner.sol"; +import { ILidoLocator } from "../../src/interfaces/ILidoLocator.sol"; import { ForkHelpersCommon } from "./Common.sol"; contract SimulateVote is Script, DeploymentFixtures, ForkHelpersCommon { @@ -53,7 +54,7 @@ contract SimulateVote is Script, DeploymentFixtures, ForkHelpersCommon { }); // 2. burner role burner.grantRole( - burner.REQUEST_BURN_SHARES_ROLE(), + burner.REQUEST_BURN_MY_STETH_ROLE(), address(accounting) ); // 3. Grant resume to agent @@ -101,9 +102,11 @@ contract SimulateVote is Script, DeploymentFixtures, ForkHelpersCommon { feeDistributorProxy.proxy__upgradeTo(upgradeConfig.feeDistributorImpl); address admin = _prepareAdmin(deploymentConfig.csm); + locator = ILidoLocator(deploymentConfig.lidoLocator); csm = CSModule(deploymentConfig.csm); accounting = CSAccounting(deploymentConfig.accounting); oracle = CSFeeOracle(deploymentConfig.oracle); + IBurner burner = IBurner(locator.burner()); vm.startBroadcast(admin); csm.revokeRole(csm.VERIFIER_ROLE(), address(deploymentConfig.verifier)); @@ -125,6 +128,14 @@ contract SimulateVote is Script, DeploymentFixtures, ForkHelpersCommon { address(upgradeConfig.gateSeal) ); oracle.grantRole(oracle.PAUSE_ROLE(), address(upgradeConfig.gateSeal)); + burner.revokeRole( + burner.REQUEST_BURN_SHARES_ROLE(), + address(accounting) + ); + burner.grantRole( + burner.REQUEST_BURN_MY_STETH_ROLE(), + address(accounting) + ); vm.stopBroadcast(); } } diff --git a/src/abstract/CSBondCore.sol b/src/abstract/CSBondCore.sol index cda15733..391c4cb4 100644 --- a/src/abstract/CSBondCore.sol +++ b/src/abstract/CSBondCore.sol @@ -179,22 +179,20 @@ abstract contract CSBondCore is ICSBondCore { } /// @dev Burn Node Operator's bond shares (stETH). Shares will be burned on the next stETH rebase - /// @dev The method sender should be granted as `Burner.REQUEST_BURN_SHARES_ROLE` and make stETH allowance for `Burner` + /// @dev The method sender should be granted as `Burner.REQUEST_BURN_MY_STETH_ROLE` and make stETH allowance for `Burner` /// @param amount Bond amount to burn in ETH (stETH) function _burn(uint256 nodeOperatorId, uint256 amount) internal { uint256 toBurnShares = _sharesByEth(amount); uint256 burnedShares = _reduceBond(nodeOperatorId, toBurnShares); // If no bond already if (burnedShares == 0) return; + uint256 burnedAmount = _ethByShares(burnedShares); - IBurner(LIDO_LOCATOR.burner()).requestBurnShares( - address(this), - burnedShares - ); + IBurner(LIDO_LOCATOR.burner()).requestBurnMyStETH(burnedAmount); emit BondBurned( nodeOperatorId, _ethByShares(toBurnShares), - _ethByShares(burnedShares) + burnedAmount ); } diff --git a/src/interfaces/IBurner.sol b/src/interfaces/IBurner.sol index 1fc9f709..ceb61bae 100644 --- a/src/interfaces/IBurner.sol +++ b/src/interfaces/IBurner.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; interface IBurner { + function REQUEST_BURN_MY_STETH_ROLE() external view returns (bytes32); + function REQUEST_BURN_SHARES_ROLE() external view returns (bytes32); function DEFAULT_ADMIN_ROLE() external view returns (bytes32); @@ -15,13 +17,12 @@ interface IBurner { function grantRole(bytes32 role, address account) external; + function revokeRole(bytes32 role, address account) external; + function hasRole( bytes32 role, address account ) external view returns (bool); - function requestBurnShares( - address _from, - uint256 _sharesAmountToBurn - ) external; + function requestBurnMyStETH(uint256 _stETHAmountToBurn) external; } diff --git a/test/CSAccounting.t.sol b/test/CSAccounting.t.sol index 4f43507c..e2cda89e 100644 --- a/test/CSAccounting.t.sol +++ b/test/CSAccounting.t.sol @@ -3708,13 +3708,13 @@ contract CSAccountingPenalizeTest is CSAccountingBaseTest { function test_penalize() public assertInvariants { uint256 shares = stETH.getSharesByPooledEth(1 ether); uint256 bondSharesBefore = accounting.getBondShares(0); + uint256 amountToBurn = stETH.getPooledEthByShares(shares); vm.expectCall( locator.burner(), abi.encodeWithSelector( - IBurner.requestBurnShares.selector, - address(accounting), - shares + IBurner.requestBurnMyStETH.selector, + amountToBurn ) ); @@ -3919,7 +3919,7 @@ contract CSAccountingLockBondETHTest is CSAccountingBaseTest { expectNoCall( address(burner), - abi.encodeWithSelector(IBurner.requestBurnShares.selector) + abi.encodeWithSelector(IBurner.requestBurnMyStETH.selector) ); accounting.settleLockedBondETH(noId); vm.stopPrank(); diff --git a/test/CSBondCore.t.sol b/test/CSBondCore.t.sol index b9b0243a..380d6107 100644 --- a/test/CSBondCore.t.sol +++ b/test/CSBondCore.t.sol @@ -491,11 +491,7 @@ contract CSBondCoreBurnTest is CSBondCoreTestBase { uint256 bondSharesBefore = bondCore.getBondShares(0); vm.expectCall( locator.burner(), - abi.encodeWithSelector( - IBurner.requestBurnShares.selector, - address(bondCore), - shares - ) + abi.encodeWithSelector(IBurner.requestBurnMyStETH.selector, burned) ); bondCore.burn(0, 1 ether); uint256 bondSharesAfter = bondCore.getBondShares(0); @@ -513,6 +509,8 @@ contract CSBondCoreBurnTest is CSBondCoreTestBase { uint256 bondSharesBefore = bondCore.getBondShares(0); uint256 burnShares = stETH.getSharesByPooledEth(33 ether); + uint256 sharesToBurn = stETH.getSharesByPooledEth(32 ether); + uint256 amountToBurn = stETH.getPooledEthByShares(sharesToBurn); vm.expectEmit(true, true, true, true, address(bondCore)); emit ICSBondCore.BondBurned( 0, @@ -523,9 +521,8 @@ contract CSBondCoreBurnTest is CSBondCoreTestBase { vm.expectCall( locator.burner(), abi.encodeWithSelector( - IBurner.requestBurnShares.selector, - address(bondCore), - stETH.getSharesByPooledEth(32 ether) + IBurner.requestBurnMyStETH.selector, + amountToBurn ) ); @@ -549,11 +546,7 @@ contract CSBondCoreBurnTest is CSBondCoreTestBase { vm.expectCall( locator.burner(), - abi.encodeWithSelector( - IBurner.requestBurnShares.selector, - address(bondCore), - shares - ) + abi.encodeWithSelector(IBurner.requestBurnMyStETH.selector, burned) ); bondCore.burn(0, 32 ether); diff --git a/test/fork/integration/Penalty.t.sol b/test/fork/integration/Penalty.t.sol index 7e179c55..bf9fc0f6 100644 --- a/test/fork/integration/Penalty.t.sol +++ b/test/fork/integration/Penalty.t.sol @@ -101,13 +101,13 @@ contract PenaltyIntegrationTest is IBurner burner = IBurner(locator.burner()); if ( !burner.hasRole( - burner.REQUEST_BURN_SHARES_ROLE(), + burner.REQUEST_BURN_MY_STETH_ROLE(), address(accounting) ) ) { vm.startPrank(burner.getRoleMember(burner.DEFAULT_ADMIN_ROLE(), 0)); burner.grantRole( - burner.REQUEST_BURN_SHARES_ROLE(), + burner.REQUEST_BURN_MY_STETH_ROLE(), address(accounting) ); vm.stopPrank(); diff --git a/test/fork/voting/StatePostVote.t.sol b/test/fork/voting/StatePostVote.t.sol index a2ac894b..5013ff78 100644 --- a/test/fork/voting/StatePostVote.t.sol +++ b/test/fork/voting/StatePostVote.t.sol @@ -74,6 +74,12 @@ contract ContractsStateTest is Test, Utilities, DeploymentFixtures { accounting.getCurveInfo(earlyAdoption.CURVE_ID()).points, deployParams.earlyAdoptionBondCurve ); + assertTrue( + burner.hasRole( + burner.REQUEST_BURN_MY_STETH_ROLE(), + address(accounting) + ) + ); } function test_accountingRoles() public { diff --git a/test/helpers/Fixtures.sol b/test/helpers/Fixtures.sol index 48c71766..cda277f5 100644 --- a/test/helpers/Fixtures.sol +++ b/test/helpers/Fixtures.sol @@ -12,6 +12,7 @@ import { Stub } from "./mocks/Stub.sol"; import "forge-std/Test.sol"; import { IStakingRouter } from "../../src/interfaces/IStakingRouter.sol"; import { ILido } from "../../src/interfaces/ILido.sol"; +import { IBurner } from "../../src/interfaces/IBurner.sol"; import { ILidoLocator } from "../../src/interfaces/ILidoLocator.sol"; import { IWstETH } from "../../src/interfaces/IWstETH.sol"; import { IGateSeal } from "../../src/interfaces/IGateSeal.sol"; @@ -224,6 +225,7 @@ contract DeploymentFixtures is StdCheats, DeploymentHelpers { IStakingRouter public stakingRouter; ILido public lido; IGateSeal public gateSeal; + IBurner public burner; function initializeFromDeployment() public { Env memory env = envVars(); @@ -245,6 +247,7 @@ contract DeploymentFixtures is StdCheats, DeploymentHelpers { stakingRouter = IStakingRouter(locator.stakingRouter()); wstETH = IWstETH(IWithdrawalQueue(locator.withdrawalQueue()).WSTETH()); gateSeal = IGateSeal(deploymentConfig.gateSeal); + burner = IBurner(locator.burner()); if (!_isEmpty(env.UPGRADE_CONFIG)) { UpgradeConfig memory upgradeConfig = parseUpgradeConfig( diff --git a/test/helpers/mocks/BurnerMock.sol b/test/helpers/mocks/BurnerMock.sol index 359e15de..abd843b1 100644 --- a/test/helpers/mocks/BurnerMock.sol +++ b/test/helpers/mocks/BurnerMock.sol @@ -12,15 +12,8 @@ contract BurnerMock { STETH = _stETH; } - function requestBurnShares( - address _from, - uint256 _sharesAmountToBurn - ) external { - IStETH(STETH).transferSharesFrom( - _from, - address(this), - _sharesAmountToBurn - ); - if (_sharesAmountToBurn == 0) revert ZeroBurnAmount(); + function requestBurnMyStETH(uint256 _amountToBurn) external { + if (_amountToBurn == 0) revert ZeroBurnAmount(); + IStETH(STETH).transferFrom(msg.sender, address(this), _amountToBurn); } }