Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e46faac
feat: add `maxValues` for `CSMSettleElStealingPenalty`
vgorkavenko Aug 12, 2025
809d9f3
feat: rename factory. introduce curated v2 things
vgorkavenko Nov 6, 2025
e5b2bf0
Merge remote-tracking branch 'origin/develop' into feat/csm-settle-el…
vgorkavenko Nov 6, 2025
a1e1300
fix
vgorkavenko Nov 6, 2025
11b8959
feat: add `name` for factory
vgorkavenko Nov 7, 2025
7ee484c
fix: name in scripts
vgorkavenko Nov 7, 2025
9947aa2
fix: more renames
vgorkavenko Nov 7, 2025
217b3dd
feat: add deployment script for CM
vgorkavenko Nov 11, 2025
dc69bce
feat: add SubmitWithdrawals factory
madlabman Nov 11, 2025
6773e4d
chore: pin solidity 0.8.6
madlabman Nov 12, 2025
396934c
chore: add name to the factory
madlabman Nov 12, 2025
c406bc8
feat: add deploy script for SubmitWithdrawals
madlabman Nov 14, 2025
14c14b7
test: add scenario/integration test for SubmitWithdrawals
madlabman Nov 14, 2025
f226362
feat: rename set vetted gate tree factory
vgorkavenko Nov 7, 2025
70cf3b0
feat: allowed gates registry
vgorkavenko Nov 13, 2025
6fb6e02
fix: review
vgorkavenko Nov 14, 2025
a66790e
chore: make allowedGates private variable
madlabman Nov 19, 2025
a91b082
test: few tweaks to allowed gate registry
madlabman Nov 19, 2025
fdb4222
fix: use the updated method
madlabman Nov 28, 2025
83b6aba
refactor: rename the factory one more time
madlabman Dec 1, 2025
ef9ee2b
Merge branch 'feat/csm-settle-el-stealing-penalty-with-max-values' in…
vgorkavenko Jan 23, 2026
a1bd033
Merge pull request #100 from lidofinance/csm-submit-withdrawals
vgorkavenko Jan 23, 2026
00642cc
fix: `reportWithdrawnValidators` -> `reportSlashedWithdrawnValidators`
vgorkavenko Jan 27, 2026
e36ddf4
feat: settle general delayed penalty deploy script
vgorkavenko Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 0 additions & 99 deletions contracts/EVMScriptFactories/CSMSettleELStealingPenalty.sol

This file was deleted.

119 changes: 119 additions & 0 deletions contracts/EVMScriptFactories/SettleGeneralDelayedPenalty.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-FileCopyrightText: 2024 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "../TrustedCaller.sol";
import "../libraries/EVMScriptCreator.sol";
import "../interfaces/IEVMScriptFactory.sol";
import "../interfaces/ICSModule.sol";
import "../interfaces/ICSAccounting.sol";

/// @author vgorkavenko
/// @notice Creates EVMScript to settle general delayed penalty for a specific node operators
contract SettleGeneralDelayedPenalty is TrustedCaller, IEVMScriptFactory {

// -------------
// ERRORS
// -------------

string private constant ERROR_EMPTY_NODE_OPERATORS_IDS =
"EMPTY_NODE_OPERATORS_IDS";
string private constant ERROR_OUT_OF_RANGE_NODE_OPERATOR_ID =
"OUT_OF_RANGE_NODE_OPERATOR_ID";
string private constant ERROR_NODE_OPERATORS_IDS_AND_MAX_AMOUNTS_LENGTH_MISMATCH =
"NODE_OPERATORS_IDS_AND_MAX_AMOUNTS_LENGTH_MISMATCH";
string private constant ERROR_MAX_AMOUNT_SHOULD_BE_GREATER_OR_EQUAL_THAN_ACTUAL_LOCKED =
"MAX_AMOUNT_SHOULD_BE_GREATER_OR_EQUAL_THAN_ACTUAL_LOCKED";
string private constant ERROR_MAX_AMOUNT_SHOULD_BE_GREATER_THAN_ZERO =
"MAX_AMOUNT_SHOULD_BE_GREATER_THAN_ZERO";

// -------------
// VARIABLES
// -------------

/// @notice Address of Module Contract
ICSModule public immutable module;
ICSAccounting public immutable accounting;

// -------------
// CONSTRUCTOR
// -------------

constructor(address _trustedCaller, address _module)
TrustedCaller(_trustedCaller)
{
module = ICSModule(_module);
accounting = ICSAccounting(ICSModule(_module).ACCOUNTING());
}

// -------------
// EXTERNAL METHODS
// -------------

/// @notice Creates EVMScript to settle general delayed penalty for the specific node operators
/// @param _creator Address who creates EVMScript
/// @param _evmScriptCallData Encoded: uint256[] memory nodeOperatorIds, uint256[] memory maxAmounts
function createEVMScript(address _creator, bytes memory _evmScriptCallData)
external
view
override
onlyTrustedCaller(_creator)
returns (bytes memory)
{
(uint256[] memory nodeOperatorIds, uint256[] memory maxAmounts) = _decodeEVMScriptCallData(_evmScriptCallData);

_validateInputData(nodeOperatorIds, maxAmounts);

return
EVMScriptCreator.createEVMScript(
address(module),
ICSModule.settleGeneralDelayedPenalty.selector,
_evmScriptCallData
);
}

/// @notice Decodes call data used by createEVMScript method
/// @param _evmScriptCallData Encoded: uint256[] memory nodeOperatorIds, uint256[] memory maxAmounts
/// @return Node operator IDs and max amounts to settle general delayed penalty
function decodeEVMScriptCallData(bytes memory _evmScriptCallData)
external
pure
returns (uint256[] memory, uint256[] memory)
{
return _decodeEVMScriptCallData(_evmScriptCallData);
}

// ------------------
// PRIVATE METHODS
// ------------------

function _decodeEVMScriptCallData(bytes memory _evmScriptCallData)
private
pure
returns (uint256[] memory, uint256[] memory)
{
return abi.decode(_evmScriptCallData, (uint256[], uint256[]));
}

function _validateInputData(
uint256[] memory nodeOperatorsIds,
uint256[] memory maxAmounts
) private view {
require(nodeOperatorsIds.length > 0, ERROR_EMPTY_NODE_OPERATORS_IDS);
require(
nodeOperatorsIds.length == maxAmounts.length,
ERROR_NODE_OPERATORS_IDS_AND_MAX_AMOUNTS_LENGTH_MISMATCH
);
uint256 nodeOperatorsCount = module.getNodeOperatorsCount();
for (uint256 i = 0; i < nodeOperatorsIds.length; ++i) {
(uint256 nodeOperatorId, uint256 maxAmount) = (nodeOperatorsIds[i], maxAmounts[i]);
require(nodeOperatorId < nodeOperatorsCount, ERROR_OUT_OF_RANGE_NODE_OPERATOR_ID);
uint256 actualLocked = accounting.getActualLockedBond(
nodeOperatorId
);
require(maxAmount > 0, ERROR_MAX_AMOUNT_SHOULD_BE_GREATER_THAN_ZERO);
require(maxAmount >= actualLocked, ERROR_MAX_AMOUNT_SHOULD_BE_GREATER_OR_EQUAL_THAN_ACTUAL_LOCKED);
}
}
}
14 changes: 14 additions & 0 deletions contracts/interfaces/ICSAccounting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/// @title Lido's CSM accounting interface
interface ICSAccounting {
/// @notice Get amount of the locked bond in ETH (stETH) by the given Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @return Amount of the actual locked bond
function getActualLockedBond(
uint256 nodeOperatorId
) external view returns (uint256);
}
9 changes: 7 additions & 2 deletions contracts/interfaces/ICSModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@

/// @title Lido's Community Staking Module interface
interface ICSModule {

function ACCOUNTING() external view returns (address);

/// @notice Settles blocked bond for the given Node Operators
/// @dev Should be called by the Easy Track
/// @param nodeOperatorIds IDs of the Node Operators
function settleELRewardsStealingPenalty(
uint256[] memory nodeOperatorIds
/// @param maxAmounts Maximum amounts to settle for each Node Operator
function settleGeneralDelayedPenalty(
uint256[] memory nodeOperatorIds,
uint256[] memory maxAmounts
) external;

function getNodeOperatorsCount() external view returns (uint256);
Expand Down
2 changes: 1 addition & 1 deletion interfaces/CSModule.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import constants
from utils.lido import contracts as lido_contracts_
from utils.csm import contracts as csm_contracts_
from utils.cm import contracts as cm_contracts_
from utils import deployed_date_time
from utils.test_helpers import set_account_balance
from utils.submit_exit_requests_test_helpers import MAX_REQUESTS
Expand Down Expand Up @@ -111,6 +112,11 @@ def csm_contracts():
return csm_contracts_(network=brownie.network.show_active())


@pytest.fixture(scope="module")
def cm_contracts():
return cm_contracts_(network=brownie.network.show_active())


@pytest.fixture(scope="module")
def motion_settings(owner, MotionSettings):
return owner.deploy(
Expand Down Expand Up @@ -431,6 +437,11 @@ def cs_module(csm_contracts):
return csm_contracts.module


@pytest.fixture(scope="module")
def curated_module(cm_contracts):
return cm_contracts.curated_module


@pytest.fixture(scope="module")
def voting(lido_contracts):
return lido_contracts.aragon.voting
Expand Down
Loading
Loading