Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/PermissionlessGate.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { AccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import { AssetRecoverer } from "./abstract/AssetRecoverer.sol";
import { ICSAccounting } from "./interfaces/ICSAccounting.sol";
import { ICSModule, NodeOperatorManagementProperties } from "./interfaces/ICSModule.sol";
import { IPermissionlessGate } from "./interfaces/IPermissionlessGate.sol";
/// @title PermissionlessGate
/// @notice Contract for adding new Node Operators without any restrictions
contract PermissionlessGate is
IPermissionlessGate,
AccessControlEnumerable,
AssetRecoverer
{
bytes32 public constant RECOVERER_ROLE = keccak256("RECOVERER_ROLE");
/// @dev Curve ID is the default bond curve ID from the accounting contract
/// This immutable variable is kept here for consistency with the other gates
uint256 public immutable CURVE_ID;
/// @dev Address of the Staking Module
ICSModule public immutable MODULE;
constructor(address module, address admin) {
if (module == address(0)) {
revert ZeroModuleAddress();
}
if (admin == address(0)) {
revert ZeroAdminAddress();
}
MODULE = ICSModule(module);
CURVE_ID = MODULE.accounting().DEFAULT_BOND_CURVE_ID();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
/// @inheritdoc IPermissionlessGate
function addNodeOperatorETH(
uint256 keysCount,
bytes calldata publicKeys,
bytes calldata signatures,
NodeOperatorManagementProperties calldata managementProperties,
address referrer
) external payable returns (uint256 nodeOperatorId) {
nodeOperatorId = MODULE.createNodeOperator({
from: msg.sender,
managementProperties: managementProperties,
referrer: referrer
});
MODULE.addValidatorKeysETH{ value: msg.value }({
from: msg.sender,
nodeOperatorId: nodeOperatorId,
keysCount: keysCount,
publicKeys: publicKeys,
signatures: signatures
});
}
/// @inheritdoc IPermissionlessGate
function addNodeOperatorStETH(
uint256 keysCount,
bytes calldata publicKeys,
bytes calldata signatures,
NodeOperatorManagementProperties calldata managementProperties,
ICSAccounting.PermitInput calldata permit,
address referrer
) external returns (uint256 nodeOperatorId) {
nodeOperatorId = MODULE.createNodeOperator({
from: msg.sender,
managementProperties: managementProperties,
referrer: referrer
});
MODULE.addValidatorKeysStETH({
from: msg.sender,
nodeOperatorId: nodeOperatorId,
keysCount: keysCount,
publicKeys: publicKeys,
signatures: signatures,
permit: permit
});
}
/// @inheritdoc IPermissionlessGate
function addNodeOperatorWstETH(
uint256 keysCount,
bytes calldata publicKeys,
bytes calldata signatures,
NodeOperatorManagementProperties calldata managementProperties,
ICSAccounting.PermitInput calldata permit,
address referrer
) external returns (uint256 nodeOperatorId) {
nodeOperatorId = MODULE.createNodeOperator({
from: msg.sender,
managementProperties: managementProperties,
referrer: referrer
});
MODULE.addValidatorKeysWstETH({
from: msg.sender,
nodeOperatorId: nodeOperatorId,
keysCount: keysCount,
publicKeys: publicKeys,
signatures: signatures,
permit: permit
});
}
function _onlyRecoverer() internal view override {
_checkRole(RECOVERER_ROLE);
}
}
"
},
"node_modules/@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
import {AccessControl} from "../AccessControl.sol";
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
bool granted = super._grantRole(role, account);
if (granted) {
_roleMembers[role].add(account);
}
return granted;
}
/**
* @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
bool revoked = super._revokeRole(role, account);
if (revoked) {
_roleMembers[role].remove(account);
}
return revoked;
}
}
"
},
"src/abstract/AssetRecoverer.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { AssetRecovererLib } from "../lib/AssetRecovererLib.sol";
/// @title AssetRecoverer
/// @dev Abstract contract providing mechanisms for recovering various asset types (ETH, ERC20, ERC721, ERC1155) from a contract.
/// This contract is designed to allow asset recovery by an authorized address by implementing the onlyRecovererRole guardian
/// @notice Assets can be sent only to the `msg.sender`
abstract contract AssetRecoverer {
/// @dev Allows sender to recover Ether held by the contract
/// Emits an EtherRecovered event upon success
function recoverEther() external {
_onlyRecoverer();
AssetRecovererLib.recoverEther();
}
/// @dev Allows sender to recover ERC20 tokens held by the contract
/// @param token The address of the ERC20 token to recover
/// @param amount The amount of the ERC20 token to recover
/// Emits an ERC20Recovered event upon success
/// Optionally, the inheriting contract can override this function to add additional restrictions
function recoverERC20(address token, uint256 amount) external virtual {
_onlyRecoverer();
AssetRecovererLib.recoverERC20(token, amount);
}
/// @dev Allows sender to recover ERC721 tokens held by the contract
/// @param token The address of the ERC721 token to recover
/// @param tokenId The token ID of the ERC721 token to recover
/// Emits an ERC721Recovered event upon success
function recoverERC721(address token, uint256 tokenId) external {
_onlyRecoverer();
AssetRecovererLib.recoverERC721(token, tokenId);
}
/// @dev Allows sender to recover ERC1155 tokens held by the contract.
/// @param token The address of the ERC1155 token to recover.
/// @param tokenId The token ID of the ERC1155 token to recover.
/// Emits an ERC1155Recovered event upon success.
function recoverERC1155(address token, uint256 tokenId) external {
_onlyRecoverer();
AssetRecovererLib.recoverERC1155(token, tokenId);
}
/// @dev Guardian to restrict access to the recover methods.
/// Should be implemented by the inheriting contract
function _onlyRecoverer() internal view virtual;
}
"
},
"src/interfaces/ICSAccounting.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { ICSBondCore } from "./ICSBondCore.sol";
import { ICSBondCurve } from "./ICSBondCurve.sol";
import { ICSBondLock } from "./ICSBondLock.sol";
import { ICSFeeDistributor } from "./ICSFeeDistributor.sol";
import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol";
import { ICSModule } from "./ICSModule.sol";
interface ICSAccounting is
ICSBondCore,
ICSBondCurve,
ICSBondLock,
IAssetRecovererLib
{
struct PermitInput {
uint256 value;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
event BondLockCompensated(uint256 indexed nodeOperatorId, uint256 amount);
event ChargePenaltyRecipientSet(address chargePenaltyRecipient);
error SenderIsNotModule();
error SenderIsNotEligible();
error ZeroModuleAddress();
error ZeroAdminAddress();
error ZeroFeeDistributorAddress();
error ZeroChargePenaltyRecipientAddress();
error NodeOperatorDoesNotExist();
error ElRewardsVaultReceiveFailed();
error InvalidBondCurvesLength();
function PAUSE_ROLE() external view returns (bytes32);
function RESUME_ROLE() external view returns (bytes32);
function MANAGE_BOND_CURVES_ROLE() external view returns (bytes32);
function SET_BOND_CURVE_ROLE() external view returns (bytes32);
function RECOVERER_ROLE() external view returns (bytes32);
function MODULE() external view returns (ICSModule);
function FEE_DISTRIBUTOR() external view returns (ICSFeeDistributor);
function feeDistributor() external view returns (ICSFeeDistributor);
function chargePenaltyRecipient() external view returns (address);
/// @notice Get the initialized version of the contract
function getInitializedVersion() external view returns (uint64);
/// @notice Resume reward claims and deposits
function resume() external;
/// @notice Pause reward claims and deposits for `duration` seconds
/// @dev Must be called together with `CSModule.pauseFor`
/// @dev Passing MAX_UINT_256 as `duration` pauses indefinitely
/// @param duration Duration of the pause in seconds
function pauseFor(uint256 duration) external;
/// @notice Set charge recipient address
/// @param _chargePenaltyRecipient Charge recipient address
function setChargePenaltyRecipient(
address _chargePenaltyRecipient
) external;
/// @notice Set bond lock period
/// @param period Period in seconds to retain bond lock
function setBondLockPeriod(uint256 period) external;
/// @notice Add a new bond curve
/// @param bondCurve Bond curve definition to add
/// @return id Id of the added curve
function addBondCurve(
BondCurveIntervalInput[] calldata bondCurve
) external returns (uint256 id);
/// @notice Update existing bond curve
/// @dev If the curve is updated to a curve with higher values for any point,
/// Extensive checks and actions should be performed by the method caller to avoid
/// inconsistency in the keys accounting. A manual update of the depositable validators count
/// in CSM might be required to ensure that the keys pointers are consistent.
/// @param curveId Bond curve ID to update
/// @param bondCurve Bond curve definition
function updateBondCurve(
uint256 curveId,
BondCurveIntervalInput[] calldata bondCurve
) external;
/// @notice Get the required bond in ETH (inc. missed and excess) for the given Node Operator to upload new deposit data
/// @param nodeOperatorId ID of the Node Operator
/// @param additionalKeys Number of new keys to add
/// @return Required bond amount in ETH
function getRequiredBondForNextKeys(
uint256 nodeOperatorId,
uint256 additionalKeys
) external view returns (uint256);
/// @notice Get the bond amount in wstETH required for the `keysCount` keys using the default bond curve
/// @param keysCount Keys count to calculate the required bond amount
/// @param curveId Id of the curve to perform calculations against
/// @return wstETH amount required for the `keysCount`
function getBondAmountByKeysCountWstETH(
uint256 keysCount,
uint256 curveId
) external view returns (uint256);
/// @notice Get the required bond in wstETH (inc. missed and excess) for the given Node Operator to upload new keys
/// @param nodeOperatorId ID of the Node Operator
/// @param additionalKeys Number of new keys to add
/// @return Required bond in wstETH
function getRequiredBondForNextKeysWstETH(
uint256 nodeOperatorId,
uint256 additionalKeys
) external view returns (uint256);
/// @notice Get the number of the unbonded keys
/// @param nodeOperatorId ID of the Node Operator
/// @return Unbonded keys count
function getUnbondedKeysCount(
uint256 nodeOperatorId
) external view returns (uint256);
/// @notice Get the number of the unbonded keys to be ejected using a forcedTargetLimit
/// Locked bond is not considered for this calculation to allow Node Operators to
/// compensate the locked bond via `compensateLockedBondETH` method before the ejection happens
/// @param nodeOperatorId ID of the Node Operator
/// @return Unbonded keys count
function getUnbondedKeysCountToEject(
uint256 nodeOperatorId
) external view returns (uint256);
/// @notice Get current and required bond amounts in ETH (stETH) for the given Node Operator
/// @dev To calculate excess bond amount subtract `required` from `current` value.
/// To calculate missed bond amount subtract `current` from `required` value
/// @param nodeOperatorId ID of the Node Operator
/// @return current Current bond amount in ETH
/// @return required Required bond amount in ETH
function getBondSummary(
uint256 nodeOperatorId
) external view returns (uint256 current, uint256 required);
/// @notice Get current and required bond amounts in stETH shares for the given Node Operator
/// @dev To calculate excess bond amount subtract `required` from `current` value.
/// To calculate missed bond amount subtract `current` from `required` value
/// @param nodeOperatorId ID of the Node Operator
/// @return current Current bond amount in stETH shares
/// @return required Required bond amount in stETH shares
function getBondSummaryShares(
uint256 nodeOperatorId
) external view returns (uint256 current, uint256 required);
/// @notice Get current claimable bond in stETH shares for the given Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @return Current claimable bond in stETH shares
function getClaimableBondShares(
uint256 nodeOperatorId
) external view returns (uint256);
/// @notice Get current claimable bond in stETH shares for the given Node Operator
/// Includes potential rewards distributed by the Fee Distributor
/// @param nodeOperatorId ID of the Node Operator
/// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator
/// @param rewardsProof Merkle proof of the rewards
/// @return Current claimable bond in stETH shares
function getClaimableRewardsAndBondShares(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external view returns (uint256);
/// @notice Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator
/// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count
/// @param from Address to unwrap wstETH from
/// @param nodeOperatorId ID of the Node Operator
/// @param wstETHAmount Amount of wstETH to deposit
/// @param permit wstETH permit for the contract
function depositWstETH(
address from,
uint256 nodeOperatorId,
uint256 wstETHAmount,
PermitInput calldata permit
) external;
/// @notice Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator
/// @dev Permissionless. Enqueues Node Operator's keys if needed
/// @param nodeOperatorId ID of the Node Operator
/// @param wstETHAmount Amount of wstETH to deposit
/// @param permit wstETH permit for the contract
function depositWstETH(
uint256 nodeOperatorId,
uint256 wstETHAmount,
PermitInput calldata permit
) external;
/// @notice Deposit user's stETH to the bond for the given Node Operator
/// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count
/// @param from Address to deposit stETH from.
/// @param nodeOperatorId ID of the Node Operator
/// @param stETHAmount Amount of stETH to deposit
/// @param permit stETH permit for the contract
function depositStETH(
address from,
uint256 nodeOperatorId,
uint256 stETHAmount,
PermitInput calldata permit
) external;
/// @notice Deposit user's stETH to the bond for the given Node Operator
/// @dev Permissionless. Enqueues Node Operator's keys if needed
/// @param nodeOperatorId ID of the Node Operator
/// @param stETHAmount Amount of stETH to deposit
/// @param permit stETH permit for the contract
function depositStETH(
uint256 nodeOperatorId,
uint256 stETHAmount,
PermitInput calldata permit
) external;
/// @notice Stake user's ETH with Lido and deposit stETH to the bond
/// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count
/// @param from Address to stake ETH and deposit stETH from
/// @param nodeOperatorId ID of the Node Operator
function depositETH(address from, uint256 nodeOperatorId) external payable;
/// @notice Stake user's ETH with Lido and deposit stETH to the bond
/// @dev Permissionless. Enqueues Node Operator's keys if needed
/// @param nodeOperatorId ID of the Node Operator
function depositETH(uint256 nodeOperatorId) external payable;
/// @notice Claim full reward (fee + bond) in stETH for the given Node Operator with desirable value.
/// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond
/// @param nodeOperatorId ID of the Node Operator
/// @param stETHAmount Amount of stETH to claim
/// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator
/// @param rewardsProof Merkle proof of the rewards
/// @return shares Amount of stETH shares claimed
/// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by
/// off-chain tooling, e.g. to make sure a tree has at least 2 leafs.
function claimRewardsStETH(
uint256 nodeOperatorId,
uint256 stETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external returns (uint256 shares);
/// @notice Claim full reward (fee + bond) in wstETH for the given Node Operator available for this moment.
/// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond
/// @param nodeOperatorId ID of the Node Operator
/// @param wstETHAmount Amount of wstETH to claim
/// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator
/// @param rewardsProof Merkle proof of the rewards
/// @return claimedWstETHAmount Amount of wstETH claimed
/// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by
/// off-chain tooling, e.g. to make sure a tree has at least 2 leafs.
function claimRewardsWstETH(
uint256 nodeOperatorId,
uint256 wstETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external returns (uint256 claimedWstETHAmount);
/// @notice Request full reward (fee + bond) in Withdrawal NFT (unstETH) for the given Node Operator available for this moment.
/// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond
/// @dev Reverts if amount isn't between `MIN_STETH_WITHDRAWAL_AMOUNT` and `MAX_STETH_WITHDRAWAL_AMOUNT`
/// @param nodeOperatorId ID of the Node Operator
/// @param stETHAmount Amount of ETH to request
/// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator
/// @param rewardsProof Merkle proof of the rewards
/// @return requestId Withdrawal NFT ID
/// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by
/// off-chain tooling, e.g. to make sure a tree has at least 2 leafs.
function claimRewardsUnstETH(
uint256 nodeOperatorId,
uint256 stETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external returns (uint256 requestId);
/// @notice Lock bond in ETH for the given Node Operator
/// @dev Called by CSM exclusively
/// @param nodeOperatorId ID of the Node Operator
/// @param amount Amount to lock in ETH (stETH)
function lockBondETH(uint256 nodeOperatorId, uint256 amount) external;
/// @notice Release locked bond in ETH for the given Node Operator
/// @dev Called by CSM exclusively
/// @param nodeOperatorId ID of the Node Operator
/// @param amount Amount to release in ETH (stETH)
function releaseLockedBondETH(
uint256 nodeOperatorId,
uint256 amount
) external;
/// @notice Settle locked bond ETH for the given Node Operator
/// @dev Called by CSM exclusively
/// @param nodeOperatorId ID of the Node Operator
function settleLockedBondETH(
uint256 nodeOperatorId
) external returns (bool);
/// @notice Compensate locked bond ETH for the given Node Operator
/// @dev Called by CSM exclusively
/// @param nodeOperatorId ID of the Node Operator
function compensateLockedBondETH(uint256 nodeOperatorId) external payable;
/// @notice Set the bond curve for the given Node Operator
/// @dev Updates depositable validators count in CSM to ensure key pointers consistency
/// @param nodeOperatorId ID of the Node Operator
/// @param curveId ID of the bond curve to set
function setBondCurve(uint256 nodeOperatorId, uint256 curveId) external;
/// @notice Penalize bond by burning stETH shares of the given Node Operator
/// @dev Penalty application has a priority over the locked bond.
/// Method call can result in the remaining bond being lower than the locked bond.
/// @param nodeOperatorId ID of the Node Operator
/// @param amount Amount to penalize in ETH (stETH)
function penalize(uint256 nodeOperatorId, uint256 amount) external;
/// @notice Charge fee from bond by transferring stETH shares of the given Node Operator to the charge recipient
/// @dev Charge confiscation has a priority over the locked bond.
/// Method call can result in the remaining bond being lower than the locked bond.
/// @param nodeOperatorId ID of the Node Operator
/// @param amount Amount to charge in ETH (stETH)
function chargeFee(uint256 nodeOperatorId, uint256 amount) external;
/// @notice Pull fees from CSFeeDistributor to the Node Operator's bond
/// @dev Permissionless method. Can be called before penalty application to ensure that rewards are also penalized
/// @param nodeOperatorId ID of the Node Operator
/// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator
/// @param rewardsProof Merkle proof of the rewards
function pullFeeRewards(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external;
/// @notice Service method to update allowance to Burner in case it has changed
function renewBurnerAllowance() external;
}
"
},
"src/interfaces/ICSModule.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { IStakingModule } from "./IStakingModule.sol";
import { ICSAccounting } from "./ICSAccounting.sol";
import { IQueueLib } from "../lib/QueueLib.sol";
import { INOAddresses } from "../lib/NOAddresses.sol";
import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol";
import { Batch } from "../lib/QueueLib.sol";
import { ILidoLocator } from "./ILidoLocator.sol";
import { IStETH } from "./IStETH.sol";
import { ICSParametersRegistry } from "./ICSParametersRegistry.sol";
import { ICSExitPenalties } from "./ICSExitPenalties.sol";
struct NodeOperator {
// All the counters below are used together e.g. in the _updateDepositableValidatorsCount
/* 1 */ uint32 totalAddedKeys; // @dev increased and decreased when removed
/* 1 */ uint32 totalWithdrawnKeys; // @dev only increased
/* 1 */ uint32 totalDepositedKeys; // @dev only increased
/* 1 */ uint32 totalVettedKeys; // @dev both increased and decreased
/* 1 */ uint32 stuckValidatorsCount; // @dev both increased and decreased
/* 1 */ uint32 depositableValidatorsCount; // @dev any value
/* 1 */ uint32 targetLimit;
/* 1 */ uint8 targetLimitMode;
/* 2 */ uint32 totalExitedKeys; // @dev only increased except for the unsafe updates
/* 2 */ uint32 enqueuedCount; // Tracks how many places are occupied by the node operator's keys in the queue.
/* 2 */ address managerAddress;
/* 3 */ address proposedManagerAddress;
/* 4 */ address rewardAddress;
/* 5 */ address proposedRewardAddress;
/* 5 */ bool extendedManagerPermissions;
/* 5 */ bool usedPriorityQueue;
}
struct NodeOperatorManagementProperties {
address managerAddress;
address rewardAddress;
bool extendedManagerPermissions;
}
struct ValidatorWithdrawalInfo {
uint256 nodeOperatorId; // @dev ID of the Node Operator
uint256 keyIndex; // @dev Index of the withdrawn key in the Node Operator's keys storage
uint256 amount; // @dev Amount of withdrawn ETH in wei
}
/// @title Lido's Community Staking Module interface
interface ICSModule is
IQueueLib,
INOAddresses,
IAssetRecovererLib,
IStakingModule
{
error CannotAddKeys();
error NodeOperatorDoesNotExist();
error SenderIsNotEligible();
error InvalidVetKeysPointer();
error ExitedKeysHigherThanTotalDeposited();
error ExitedKeysDecrease();
error InvalidInput();
error NotEnoughKeys();
error PriorityQueueAlreadyUsed();
error NotEligibleForPriorityQueue();
error PriorityQueueMaxDepositsUsed();
error NoQueuedKeysToMigrate();
error KeysLimitExceeded();
error SigningKeysInvalidOffset();
error InvalidAmount();
error ZeroLocatorAddress();
error ZeroAccountingAddress();
error ZeroExitPenaltiesAddress();
error ZeroAdminAddress();
error ZeroSenderAddress();
error ZeroParametersRegistryAddress();
event NodeOperatorAdded(
uint256 indexed nodeOperatorId,
address indexed managerAddress,
address indexed rewardAddress,
bool extendedManagerPermissions
);
event ReferrerSet(uint256 indexed nodeOperatorId, address indexed referrer);
event DepositableSigningKeysCountChanged(
uint256 indexed nodeOperatorId,
uint256 depositableKeysCount
);
event VettedSigningKeysCountChanged(
uint256 indexed nodeOperatorId,
uint256 vettedKeysCount
);
event VettedSigningKeysCountDecreased(uint256 indexed nodeOperatorId);
event DepositedSigningKeysCountChanged(
uint256 indexed nodeOperatorId,
uint256 depositedKeysCount
);
event ExitedSigningKeysCountChanged(
uint256 indexed nodeOperatorId,
uint256 exitedKeysCount
);
event TotalSigningKeysCountChanged(
uint256 indexed nodeOperatorId,
uint256 totalKeysCount
);
event TargetValidatorsCountChanged(
uint256 indexed nodeOperatorId,
uint256 targetLimitMode,
uint256 targetValidatorsCount
);
event WithdrawalSubmitted(
uint256 indexed nodeOperatorId,
uint256 keyIndex,
uint256 amount,
bytes pubkey
);
event BatchEnqueued(
uint256 indexed queuePriority,
uint256 indexed nodeOperatorId,
uint256 count
);
event KeyRemovalChargeApplied(uint256 indexed nodeOperatorId);
event ELRewardsStealingPenaltyReported(
uint256 indexed nodeOperatorId,
bytes32 proposedBlockHash,
uint256 stolenAmount
);
event ELRewardsStealingPenaltyCancelled(
uint256 indexed nodeOperatorId,
uint256 amount
);
event ELRewardsStealingPenaltyCompensated(
uint256 indexed nodeOperatorId,
uint256 amount
);
event ELRewardsStealingPenaltySettled(uint256 indexed nodeOperatorId);
function PAUSE_ROLE() external view returns (bytes32);
function RESUME_ROLE() external view returns (bytes32);
function STAKING_ROUTER_ROLE() external view returns (bytes32);
function REPORT_EL_REWARDS_STEALING_PENALTY_ROLE()
external
view
returns (bytes32);
function SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE()
external
view
returns (bytes32);
function VERIFIER_ROLE() external view returns (bytes32);
function RECOVERER_ROLE() external view returns (bytes32);
function CREATE_NODE_OPERATOR_ROLE() external view returns (bytes32);
function DEPOSIT_SIZE() external view returns (uint256);
function LIDO_LOCATOR() external view returns (ILidoLocator);
function STETH() external view returns (IStETH);
function PARAMETERS_REGISTRY()
external
view
returns (ICSParametersRegistry);
function ACCOUNTING() external view returns (ICSAccounting);
function EXIT_PENALTIES() external view returns (ICSExitPenalties);
function FEE_DISTRIBUTOR() external view returns (address);
function QUEUE_LOWEST_PRIORITY() external view returns (uint256);
function QUEUE_LEGACY_PRIORITY() external view returns (uint256);
/// @notice Returns the address of the accounting contract
function accounting() external view returns (ICSAccounting);
/// @notice Pause creation of the Node Operators and keys upload for `duration` seconds.
/// Existing NO management and reward claims are still available.
/// To pause reward claims use pause method on CSAccounting
/// @param duration Duration of the pause in seconds
function pauseFor(uint256 duration) external;
/// @notice Resume creation of the Node Operators and keys upload
function resume() external;
/// @notice Returns the initialized version of the contract
function getInitializedVersion() external view returns (uint64);
/// @notice Permissioned method to add a new Node Operator
/// Should be called by `*Gate.sol` contracts. See `PermissionlessGate.sol` and `VettedGate.sol` for examples
/// @param from Sender address. Initial sender address to be used as a default manager and reward addresses.
/// Gates must pass the correct address in order to specify which address should be the owner of the Node Operator.
/// @param managementProperties Optional. Management properties to be used for the Node Operator.
/// managerAddress: Used as `managerAddress` for the Node Operator. If not passed `from` will be used.
/// rewardAddress: Used as `rewardAddress` for the Node Operator. If not passed `from` will be used.
/// extendedManagerPermissions: Flag indicating that `managerAddress` will be able to change `rewardAddress`.
/// If set to true `resetNodeOperatorManagerAddress` method will be disabled
/// @param referrer Optional. Referrer address. Should be passed when Node Operator is created using partners integration
function createNodeOperator(
address from,
NodeOperatorManagementProperties memory managementProperties,
address referrer
) external returns (uint256 nodeOperatorId);
/// @notice Add new keys to the existing Node Operator using ETH as a bond
/// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts
/// @param nodeOperatorId ID of the Node Operator
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
function addValidatorKeysETH(
address from,
uint256 nodeOperatorId,
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures
) external payable;
/// @notice Add new keys to the existing Node Operator using stETH as a bond
/// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert
/// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts
/// @param nodeOperatorId ID of the Node Operator
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
/// @param permit Optional. Permit to use stETH as bond
function addValidatorKeysStETH(
address from,
uint256 nodeOperatorId,
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures,
ICSAccounting.PermitInput memory permit
) external;
/// @notice Add new keys to the existing Node Operator using wstETH as a bond
/// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert
/// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts
/// @param nodeOperatorId ID of the Node Operator
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
/// @param permit Optional. Permit to use wstETH as bond
function addValidatorKeysWstETH(
address from,
uint256 nodeOperatorId,
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures,
ICSAccounting.PermitInput memory permit
) external;
/// @notice Report EL rewards stealing for the given Node Operator
/// @notice The final locked amount will be equal to the stolen funds plus EL stealing additional fine
/// @param nodeOperatorId ID of the Node Operator
/// @param blockHash Execution layer block hash of the proposed block with EL rewards stealing
/// @param amount Amount of stolen EL rewards in ETH
function reportELRewardsStealingPenalty(
uint256 nodeOperatorId,
bytes32 blockHash,
uint256 amount
) external;
/// @notice Compensate EL rewards stealing penalty for the given Node Operator to prevent further validator exits
/// @dev Can only be called by the Node Operator manager
/// @param nodeOperatorId ID of the Node Operator
function compensateELRewardsStealingPenalty(
uint256 nodeOperatorId
) external payable;
/// @notice Cancel previously reported and not settled EL rewards stealing penalty for the given Node Operator
/// @notice The funds will be unlocked
/// @param nodeOperatorId ID of the Node Operator
/// @param amount Amount of penalty to cancel
function cancelELRewardsStealingPenalty(
uint256 nodeOperatorId,
uint256 amount
) external;
/// @notice Settle locked bond for the given Node Operators
/// @dev SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE role is expected to be assigned to Easy Track
/// @param nodeOperatorIds IDs of the Node Operators
function settleELRewardsStealingPenalty(
uint256[] memory nodeOperatorIds
) external;
/// @notice Propose a new manager address for the Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @param proposedAddress Proposed manager address
function proposeNodeOperatorManagerAddressChange(
uint256 nodeOperatorId,
address proposedAddress
) external;
/// @notice Confirm a new manager address for the Node Operator.
/// Should be called from the currently proposed address
/// @param nodeOperatorId ID of the Node Operator
function confirmNodeOperatorManagerAddressChange(
uint256 nodeOperatorId
) external;
/// @notice Reset the manager address to the reward address.
/// Should be called from the reward address
/// @param nodeOperatorId ID of the Node Operator
function resetNodeOperatorManagerAddress(uint256 nodeOperatorId) external;
/// @notice Propose a new reward address for the Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @param proposedAddress Proposed reward address
function proposeNodeOperatorRewardAddressChange(
uint256 nodeOperatorId,
address proposedAddress
) external;
/// @notice Confirm a new reward address for the Node Operator.
/// Should be called from the currently proposed address
/// @param nodeOperatorId ID of the Node Operator
function confirmNodeOperatorRewardAddressChange(
uint256 nodeOperatorId
) external;
/// @notice Change rewardAddress if extendedManagerPermissions is enabled for the Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @param newAddress Proposed reward address
function changeNodeOperatorRewardAddress(
uint256 nodeOperatorId,
address newAddress
) external;
/// @notice Get the pointers to the head and tail of queue with the given priority.
/// @param queuePriority Priority of the queue to get the pointers.
/// @return head Pointer to the head of the queue.
/// @return tail Pointer to the tail of the queue.
function depositQueuePointers(
uint256 queuePriority
) external view returns (uint128 head, uint128 tail);
/// @notice Get the deposit queue item by an index
/// @param queuePriority Priority of the queue to get an item from
/// @param index Index of a queue item
/// @return Deposit queue item from the priority queue
function depositQueueItem(
uint256 queuePriority,
uint128 index
) external view returns (Batch);
/// @notice Clean the deposit queue from batches with no depositable keys
/// @dev Use **eth_call** to check how many items will be removed
/// @param maxItems How many queue items to review
/// @return removed Count of batches to be removed by visiting `maxItems` batches
/// @return lastRemovedAtDepth The value to use as `maxItems` to remove `removed` batches if the static call of the method was used
function cleanDepositQueue(
uint256 maxItems
) external returns (uint256 removed, uint256 lastRemovedAtDepth);
/// @notice Update depositable validators data and enqueue all unqueued keys for the given Node Operator.
/// Unqueued stands for vetted but not enqueued keys.
/// @dev The following rules are applied:
/// - Unbonded keys can not be depositable
/// - Unvetted keys can not be depositable
/// - Depositable keys count should respect targetLimit value
/// @param nodeOperatorId ID of the Node Operator
function updateDepositableValidatorsCount(uint256 nodeOperatorId) external;
/// Performs a one-time migration of allocated seats from the legacy or default queue to a priority queue
/// for an eligible node operator. This is possible, e.g., in the following scenario: A node
/// operator uploaded keys before CSM v2 and have no deposits due to a long queue.
/// After the CSM v2 release, the node operator has claimed the ICS or other priority node operator type.
/// This node operator type gives the node operator the ability to get several deposits through
/// the priority queue. So, by calling the migration method, the node operator can obtain seats
/// in the priority queue, even though they already have seats in the legacy queue.
/// The method can also be used by the node operators who joined CSM v2 permissionlessly after the release
/// and had their node operator type upgraded to ICS or another priority type.
/// The method does not remove the old queue items. Hence, the node operator can upload additional keys that
/// will take the place of the migrated keys in the original queue.
/// @param nodeOperatorId ID of the Node Operator
function migrateToPriorityQueue(uint256 nodeOperatorId) external;
/// @notice Get Node Operator info
/// @param nodeOperatorId ID of the Node Operator
/// @return Node Operator info
function getNodeOperator(
uint256 nodeOperatorId
) external view returns (NodeOperator memory);
/// @notice Get Node Operator management properties
/// @param nodeOperatorId ID of the Node Operator
/// @return Node Operator management properties
function getNodeOperatorManagementProperties(
uint256 nodeOperatorId
) external view returns (NodeOperatorManagementProperties memory);
/// @notice Get Node Operator owner. Owner is manager address if `extendedManagerPermissions` is enabled and reward address otherwise
/// @param nodeOperatorId ID of the Node Operator
/// @return Node Operator owner
function getNodeOperatorOwner(
uint256 nodeOperatorId
) external view returns (address);
/// @notice Get Node Operator non-withdrawn keys
/// @param nodeOperatorId ID of the Node Operator
/// @return Non-withdrawn keys count
function getNodeOperatorNonWithdrawnKeys(
uint256 nodeOperatorId
) external view returns (uint256);
/// @notice Get Node Operator total deposited keys
/// @param nodeOperatorId ID of the Node Operator
/// @return Total deposited keys count
function getNodeOperatorTotalDepositedKeys(
uint256 nodeOperatorId
) external view returns (uint256);
/// @notice Get Node Operator signing keys
/// @param nodeOperatorId ID of the Node Operator
/// @param startIndex Index of the first key
/// @param keysCount Count of keys to get
/// @return Signing keys
function getSigningKeys(
uint256 nodeOperatorId,
uint256 startIndex,
uint256 keysCount
) external view returns (bytes memory);
/// @notice Get Node Operator signing keys with signatures
/// @param nodeOperatorId ID of the Node Operator
/// @param startIndex Index of the first key
/// @param keysCount Count of keys to get
/// @return keys Signing keys
/// @return signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
function getSigningKeysWithSignatures(
uint256 nodeOperatorId,
uint256 startIndex,
uint256 keysCount
) external view returns (bytes memory keys, bytes memory signatures);
/// @notice Report Node Operator's keys as withdrawn and settle withdrawn amount
/// @notice Called by `CSVerifier` contract.
/// See `CSVerifier.processWithdrawalProof` to use this method permissionless
/// @param withdrawalsInfo An array for the validator withdrawals info structs
function submitWithdrawals(
ValidatorWithdrawalInfo[] calldata withdrawalsInfo
) external;
/// @notice Check if the given Node Operator's key is reported as withdrawn
/// @param nodeOperatorId ID of the Node Operator
/// @param keyIndex index of the key to check
/// @return Is validator reported as withdrawn or not
function isValidatorWithdrawn(
uint256 nodeOperatorId,
uint256 keyIndex
) external view returns (bool);
/// @notice Remove keys for the Node Operator and confiscate removal charge for each deleted key
/// This method is a part of the Optimistic Vetting scheme. After key deletion `totalVettedKeys`
/// is set equal to `totalAddedKeys`. If invalid keys are not removed, the unvetting process will be repeated
/// and `decreaseVettedSigningKeysCount` will be called by StakingRouter.
/// @param nodeOperatorId ID of the Node Operator
/// @param startIndex Index of the first key
/// @param keysCount Keys count to delete
function removeKeys(
uint256 nodeOperatorId,
uint256 startIndex,
uint256 keysCount
) external;
}
"
},
"src/interfaces/IPermissionlessGate.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { ICSModule, NodeOperatorManagementProperties } from "./ICSModule.sol";
import { ICSAccounting } from "./ICSAccounting.sol";
interface IPermissionlessGate {
error ZeroModuleAddress();
error ZeroAdminAddress();
function RECOVERER_ROLE() external view returns (bytes32);
function CURVE_ID() external view returns (uint256);
function MODULE() external view returns (ICSModule);
/// @notice Add a new Node Operator using ETH as a bond.
/// At least one deposit data and corresponding bond should be provided
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
/// @param managementProperties Optional. Management properties to be used for the Node Operator.
/// managerAddress: Used as `managerAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// rewardAddress: Used as `rewardAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// extendedManagerPermissions: Flag indicating that `managerAddress` will be able to change `rewardAddress`.
/// If set to true `resetNodeOperatorManagerAddress` method will be disabled
/// @param referrer Optional. Referrer address. Should be passed when Node Operator is created using partners integration
/// @return nodeOperatorId Id of the created Node Operator
function addNodeOperatorETH(
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures,
NodeOperatorManagementProperties memory managementProperties,
address referrer
) external payable returns (uint256 nodeOperatorId);
/// @notice Add a new Node Operator using stETH as a bond.
/// At least one deposit data and corresponding bond should be provided
/// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
/// @param managementProperties Optional. Management properties to be used for the Node Operator.
/// managerAddress: Used as `managerAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// rewardAddress: Used as `rewardAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// extendedManagerPermissions: Flag indicating that `managerAddress` will be able to change `rewardAddress`.
/// If set to true `resetNodeOperatorManagerAddress` method will be disabled
/// @param permit Optional. Permit to use stETH as bond
/// @param referrer Optional. Referrer address. Should be passed when Node Operator is created using partners integration
/// @return nodeOperatorId Id of the created Node Operator
function addNodeOperatorStETH(
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures,
NodeOperatorManagementProperties memory managementProperties,
ICSAccounting.PermitInput memory permit,
address referrer
) external returns (uint256 nodeOperatorId);
/// @notice Add a new Node Operator using wstETH as a bond.
/// At least one deposit data and corresponding bond should be provided
/// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert
/// @param keysCount Signing keys count
/// @param publicKeys Public keys to submit
/// @param signatures Signatures of `(deposit_message_root, domain)` tuples
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata
/// @param managementProperties Optional. Management properties to be used for the Node Operator.
/// managerAddress: Used as `managerAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// rewardAddress: Used as `rewardAddress` for the Node Operator. If not passed `msg.sender` will be used.
/// extendedManagerPermissions: Flag indicating that `managerAddress` will be able to change `rewardAddress`.
/// If set to true `resetNodeOperatorManagerAddress` method will be disabled
/// @param permit Optional. Permit to use wstETH as bond
/// @param referrer Optional. Referrer address. Should be passed when Node Operator is created using partners integration
/// @return nodeOperatorId Id of the created Node Operator
function addNodeOperatorWstETH(
uint256 keysCount,
bytes memory publicKeys,
bytes memory signatures,
NodeOperatorManagementProperties memory managementProperties,
ICSAccounting.PermitInput memory permit,
address referrer
) external returns (uint256 nodeOperatorId);
}
"
},
"node_modules/@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
"
},
"node_modules/@openzeppelin/contracts/access/AccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
Submitted on: 2025-09-17 15:04:52
Comments
Log in to comment.
No comments yet.