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/CSFeeOracle.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { AssetRecoverer } from "./abstract/AssetRecoverer.sol";
import { PausableUntil } from "./lib/utils/PausableUntil.sol";
import { BaseOracle } from "./lib/base-oracle/BaseOracle.sol";
import { ICSFeeDistributor } from "./interfaces/ICSFeeDistributor.sol";
import { ICSStrikes } from "./interfaces/ICSStrikes.sol";
import { ICSFeeOracle } from "./interfaces/ICSFeeOracle.sol";
contract CSFeeOracle is
ICSFeeOracle,
BaseOracle,
PausableUntil,
AssetRecoverer
{
/// @notice No assets are stored in the contract
/// @notice An ACL role granting the permission to submit the data for a committee report.
bytes32 public constant SUBMIT_DATA_ROLE = keccak256("SUBMIT_DATA_ROLE");
/// @notice An ACL role granting the permission to pause accepting oracle reports
bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");
/// @notice An ACL role granting the permission to resume accepting oracle reports
bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE");
/// @notice An ACL role granting the permission to recover assets
bytes32 public constant RECOVERER_ROLE = keccak256("RECOVERER_ROLE");
ICSFeeDistributor public immutable FEE_DISTRIBUTOR;
ICSStrikes public immutable STRIKES;
/// @dev DEPRECATED
/// @custom:oz-renamed-from feeDistributor
ICSFeeDistributor internal _feeDistributor;
/// @dev DEPRECATED
/// @custom:oz-renamed-from avgPerfLeewayBP
uint256 internal _avgPerfLeewayBP;
constructor(
address feeDistributor,
address strikes,
uint256 secondsPerSlot,
uint256 genesisTime
) BaseOracle(secondsPerSlot, genesisTime) {
if (feeDistributor == address(0)) {
revert ZeroFeeDistributorAddress();
}
if (strikes == address(0)) {
revert ZeroStrikesAddress();
}
FEE_DISTRIBUTOR = ICSFeeDistributor(feeDistributor);
STRIKES = ICSStrikes(strikes);
}
/// @dev initialize contract from scratch
function initialize(
address admin,
address consensusContract,
uint256 consensusVersion
) external {
if (admin == address(0)) {
revert ZeroAdminAddress();
}
_grantRole(DEFAULT_ADMIN_ROLE, admin);
BaseOracle._initialize(consensusContract, consensusVersion, 0);
_updateContractVersion(2);
}
/// @dev This method is expected to be called only when the contract is upgraded from version 1 to version 2 for the existing version 1 deployment.
/// If the version 2 contract is deployed from scratch, the `initialize` method should be used instead.
function finalizeUpgradeV2(uint256 consensusVersion) external {
_setConsensusVersion(consensusVersion);
// nullify storage slots
assembly ("memory-safe") {
sstore(_feeDistributor.slot, 0x00)
sstore(_avgPerfLeewayBP.slot, 0x00)
}
_updateContractVersion(2);
}
/// @inheritdoc ICSFeeOracle
function resume() external onlyRole(RESUME_ROLE) {
_resume();
}
/// @inheritdoc ICSFeeOracle
function pauseFor(uint256 duration) external onlyRole(PAUSE_ROLE) {
_pauseFor(duration);
}
/// @inheritdoc ICSFeeOracle
function submitReportData(
ReportData calldata data,
uint256 contractVersion
) external whenResumed {
_checkMsgSenderIsAllowedToSubmitData();
_checkContractVersion(contractVersion);
_checkConsensusData(
data.refSlot,
data.consensusVersion,
// it's a waste of gas to copy the whole calldata into mem but seems there's no way around
keccak256(abi.encode(data))
);
_startProcessing();
_handleConsensusReportData(data);
}
/// @dev Called in `submitConsensusReport` after a consensus is reached.
function _handleConsensusReport(
ConsensusReport memory /* report */,
uint256 /* prevSubmittedRefSlot */,
uint256 /* prevProcessingRefSlot */
) internal override {
// solhint-disable-previous-line no-empty-blocks
// We do not require any type of async processing so far, so no actions required.
}
function _handleConsensusReportData(ReportData calldata data) internal {
FEE_DISTRIBUTOR.processOracleReport({
_treeRoot: data.treeRoot,
_treeCid: data.treeCid,
_logCid: data.logCid,
distributed: data.distributed,
rebate: data.rebate,
refSlot: data.refSlot
});
STRIKES.processOracleReport(data.strikesTreeRoot, data.strikesTreeCid);
}
function _checkMsgSenderIsAllowedToSubmitData() internal view {
if (
_isConsensusMember(msg.sender) ||
hasRole(SUBMIT_DATA_ROLE, msg.sender)
) {
return;
}
revert SenderNotAllowed();
}
function _onlyRecoverer() internal view override {
_checkRole(RECOVERER_ROLE);
}
}
"
},
"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/lib/utils/PausableUntil.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { UnstructuredStorage } from "../UnstructuredStorage.sol";
contract PausableUntil {
using UnstructuredStorage for bytes32;
/// Contract resume/pause control storage slot
bytes32 internal constant RESUME_SINCE_TIMESTAMP_POSITION =
keccak256("lido.PausableUntil.resumeSinceTimestamp");
/// Special value for the infinite pause
uint256 public constant PAUSE_INFINITELY = type(uint256).max;
/// @notice Emitted when paused by the `pauseFor` or `pauseUntil` call
event Paused(uint256 duration);
/// @notice Emitted when resumed by the `resume` call
event Resumed();
error ZeroPauseDuration();
error PausedExpected();
error ResumedExpected();
error PauseUntilMustBeInFuture();
/// @notice Reverts when resumed
modifier whenPaused() {
_checkPaused();
_;
}
/// @notice Reverts when paused
modifier whenResumed() {
_checkResumed();
_;
}
/// @notice Returns one of:
/// - PAUSE_INFINITELY if paused infinitely returns
/// - first second when get contract get resumed if paused for specific duration
/// - some timestamp in past if not paused
function getResumeSinceTimestamp() external view returns (uint256) {
return RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256();
}
/// @notice Returns whether the contract is paused
function isPaused() public view returns (bool) {
return
block.timestamp <
RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256();
}
function _resume() internal {
_checkPaused();
RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(block.timestamp);
emit Resumed();
}
function _pauseFor(uint256 duration) internal {
_checkResumed();
if (duration == 0) {
revert ZeroPauseDuration();
}
uint256 resumeSince;
if (duration == PAUSE_INFINITELY) {
resumeSince = PAUSE_INFINITELY;
} else {
resumeSince = block.timestamp + duration;
}
_setPausedState(resumeSince);
}
function _pauseUntil(uint256 pauseUntilInclusive) internal {
_checkResumed();
if (pauseUntilInclusive < block.timestamp) {
revert PauseUntilMustBeInFuture();
}
uint256 resumeSince;
if (pauseUntilInclusive != PAUSE_INFINITELY) {
resumeSince = pauseUntilInclusive + 1;
} else {
resumeSince = PAUSE_INFINITELY;
}
_setPausedState(resumeSince);
}
function _setPausedState(uint256 resumeSince) internal {
RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(resumeSince);
if (resumeSince == PAUSE_INFINITELY) {
emit Paused(PAUSE_INFINITELY);
} else {
emit Paused(resumeSince - block.timestamp);
}
}
function _checkPaused() internal view {
if (!isPaused()) {
revert PausedExpected();
}
}
function _checkResumed() internal view {
if (isPaused()) {
revert ResumedExpected();
}
}
}
"
},
"src/lib/base-oracle/BaseOracle.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { UnstructuredStorage } from "../UnstructuredStorage.sol";
import { Versioned } from "../utils/Versioned.sol";
import { IReportAsyncProcessor } from "./interfaces/IReportAsyncProcessor.sol";
import { IConsensusContract } from "./interfaces/IConsensusContract.sol";
// solhint-disable ordering
abstract contract BaseOracle is
IReportAsyncProcessor,
AccessControlEnumerableUpgradeable,
Versioned
{
using UnstructuredStorage for bytes32;
using SafeCast for uint256;
struct ConsensusReport {
bytes32 hash;
uint64 refSlot;
uint64 processingDeadlineTime;
}
/// @notice An ACL role granting the permission to set the consensus
/// contract address by calling setConsensusContract.
bytes32 public constant MANAGE_CONSENSUS_CONTRACT_ROLE =
keccak256("MANAGE_CONSENSUS_CONTRACT_ROLE");
/// @notice An ACL role granting the permission to set the consensus
/// version by calling setConsensusVersion.
bytes32 public constant MANAGE_CONSENSUS_VERSION_ROLE =
keccak256("MANAGE_CONSENSUS_VERSION_ROLE");
/// @dev Storage slot: address consensusContract
bytes32 internal constant CONSENSUS_CONTRACT_POSITION =
keccak256("lido.BaseOracle.consensusContract");
/// @dev Storage slot: uint256 consensusVersion
bytes32 internal constant CONSENSUS_VERSION_POSITION =
keccak256("lido.BaseOracle.consensusVersion");
/// @dev Storage slot: uint256 lastProcessingRefSlot
bytes32 internal constant LAST_PROCESSING_REF_SLOT_POSITION =
keccak256("lido.BaseOracle.lastProcessingRefSlot");
/// @dev Storage slot: ConsensusReport consensusReport
bytes32 internal constant CONSENSUS_REPORT_POSITION =
keccak256("lido.BaseOracle.consensusReport");
uint256 public immutable SECONDS_PER_SLOT;
uint256 public immutable GENESIS_TIME;
event ConsensusHashContractSet(
address indexed addr,
address indexed prevAddr
);
event ConsensusVersionSet(
uint256 indexed version,
uint256 indexed prevVersion
);
event ReportSubmitted(
uint256 indexed refSlot,
bytes32 hash,
uint256 processingDeadlineTime
);
event ReportDiscarded(uint256 indexed refSlot, bytes32 hash);
event ProcessingStarted(uint256 indexed refSlot, bytes32 hash);
event WarnProcessingMissed(uint256 indexed refSlot);
error AddressCannotBeZero();
error AddressCannotBeSame();
error VersionCannotBeSame();
error VersionCannotBeZero();
error UnexpectedChainConfig();
error SenderIsNotTheConsensusContract();
error InitialRefSlotCannotBeLessThanProcessingOne(
uint256 initialRefSlot,
uint256 processingRefSlot
);
error RefSlotMustBeGreaterThanProcessingOne(
uint256 refSlot,
uint256 processingRefSlot
);
error RefSlotCannotDecrease(uint256 refSlot, uint256 prevRefSlot);
error NoConsensusReportToProcess();
error ProcessingDeadlineMissed(uint256 deadline);
error RefSlotAlreadyProcessing();
error UnexpectedRefSlot(uint256 consensusRefSlot, uint256 dataRefSlot);
error UnexpectedConsensusVersion(
uint256 expectedVersion,
uint256 receivedVersion
);
error HashCannotBeZero();
error UnexpectedDataHash(bytes32 consensusHash, bytes32 receivedHash);
error SecondsPerSlotCannotBeZero();
///
/// Initialization & admin functions
///
constructor(uint256 secondsPerSlot, uint256 genesisTime) {
if (secondsPerSlot == 0) {
revert SecondsPerSlotCannotBeZero();
}
SECONDS_PER_SLOT = secondsPerSlot;
GENESIS_TIME = genesisTime;
}
/// @notice Returns the address of the HashConsensus contract.
///
function getConsensusContract() external view returns (address) {
return CONSENSUS_CONTRACT_POSITION.getStorageAddress();
}
/// @notice Sets the address of the HashConsensus contract.
///
function setConsensusContract(
address addr
) external onlyRole(MANAGE_CONSENSUS_CONTRACT_ROLE) {
_setConsensusContract(
addr,
LAST_PROCESSING_REF_SLOT_POSITION.getStorageUint256()
);
}
/// @notice Returns the current consensus version expected by the oracle contract.
///
/// Consensus version must change every time consensus rules change, meaning that
/// an oracle looking at the same reference slot would calculate a different hash.
///
function getConsensusVersion() external view returns (uint256) {
return CONSENSUS_VERSION_POSITION.getStorageUint256();
}
/// @notice Sets the consensus version expected by the oracle contract.
///
function setConsensusVersion(
uint256 version
) external onlyRole(MANAGE_CONSENSUS_VERSION_ROLE) {
_setConsensusVersion(version);
}
///
/// Data provider interface
///
/// @notice Returns the last consensus report hash and metadata.
/// @dev Zero hash means that either there have been no reports yet, or the report for `refSlot` was discarded.
function getConsensusReport()
external
view
returns (
bytes32 hash,
uint256 refSlot,
uint256 processingDeadlineTime,
bool processingStarted
)
{
ConsensusReport memory report = _storageConsensusReport().value;
uint256 processingRefSlot = LAST_PROCESSING_REF_SLOT_POSITION
.getStorageUint256();
return (
report.hash,
report.refSlot,
report.processingDeadlineTime,
report.hash != bytes32(0) && report.refSlot == processingRefSlot
);
}
///
/// Consensus contract interface
///
/// @notice Called by HashConsensus contract to push a consensus report for processing.
///
/// Note that submitting the report doesn't require the processor to start processing it right
/// away, this can happen later (see `getLastProcessingRefSlot`). Until processing is started,
/// HashConsensus is free to reach consensus on another report for the same reporting frame an
/// submit it using this same function, or to lose the consensus on the submitted report,
/// notifying the processor via `discardConsensusReport`.
///
function submitConsensusReport(
bytes32 reportHash,
uint256 refSlot,
uint256 deadline
) external {
_checkSenderIsConsensusContract();
uint256 prevSubmittedRefSlot = _storageConsensusReport().value.refSlot;
if (refSlot < prevSubmittedRefSlot) {
revert RefSlotCannotDecrease(refSlot, prevSubmittedRefSlot);
}
uint256 prevProcessingRefSlot = LAST_PROCESSING_REF_SLOT_POSITION
.getStorageUint256();
if (refSlot <= prevProcessingRefSlot) {
revert RefSlotMustBeGreaterThanProcessingOne(
refSlot,
prevProcessingRefSlot
);
}
if (_getTime() > deadline) {
revert ProcessingDeadlineMissed(deadline);
}
if (
refSlot != prevSubmittedRefSlot &&
prevProcessingRefSlot != prevSubmittedRefSlot
) {
emit WarnProcessingMissed(prevSubmittedRefSlot);
}
if (reportHash == bytes32(0)) {
revert HashCannotBeZero();
}
emit ReportSubmitted(refSlot, reportHash, deadline);
ConsensusReport memory report = ConsensusReport({
hash: reportHash,
refSlot: refSlot.toUint64(),
processingDeadlineTime: deadline.toUint64()
});
_storageConsensusReport().value = report;
_handleConsensusReport(
report,
prevSubmittedRefSlot,
prevProcessingRefSlot
);
}
/// @notice Called by HashConsensus contract to notify that the report for the given ref. slot
/// is not a consensus report anymore and should be discarded. This can happen when a member
/// changes their report, is removed from the set, or when the quorum value gets increased.
///
/// Only called when, for the given reference slot:
///
/// 1. there previously was a consensus report; AND
/// 2. processing of the consensus report hasn't started yet; AND
/// 3. report processing deadline is not expired yet (enforced by HashConsensus); AND
/// 4. there's no consensus report now (otherwise, `submitConsensusReport` is called instead) (enforced by HashConsensus).
///
/// Can be called even when there's no submitted non-discarded consensus report for the current
/// reference slot, i.e. can be called multiple times in succession.
///
function discardConsensusReport(uint256 refSlot) external {
_checkSenderIsConsensusContract();
ConsensusReport memory submittedReport = _storageConsensusReport()
.value;
if (refSlot < submittedReport.refSlot) {
revert RefSlotCannotDecrease(refSlot, submittedReport.refSlot);
} else if (refSlot > submittedReport.refSlot) {
return;
}
uint256 lastProcessingRefSlot = LAST_PROCESSING_REF_SLOT_POSITION
.getStorageUint256();
if (refSlot <= lastProcessingRefSlot) {
revert RefSlotAlreadyProcessing();
}
_storageConsensusReport().value.hash = bytes32(0);
_handleConsensusReportDiscarded(submittedReport);
emit ReportDiscarded(submittedReport.refSlot, submittedReport.hash);
}
/// @notice Returns the last reference slot for which processing of the report was started.
///
function getLastProcessingRefSlot() external view returns (uint256) {
return LAST_PROCESSING_REF_SLOT_POSITION.getStorageUint256();
}
///
/// Descendant contract interface
///
/// @notice Initializes the contract storage. Must be called by a descendant
/// contract as part of its initialization.
///
function _initialize(
address consensusContract,
uint256 consensusVersion,
uint256 lastProcessingRefSlot
) internal virtual {
_initializeContractVersionTo(1);
_setConsensusContract(consensusContract, lastProcessingRefSlot);
_setConsensusVersion(consensusVersion);
LAST_PROCESSING_REF_SLOT_POSITION.setStorageUint256(
lastProcessingRefSlot
);
_storageConsensusReport().value.refSlot = lastProcessingRefSlot
.toUint64();
}
/// @notice Returns whether the given address is a member of the oracle committee.
///
function _isConsensusMember(address addr) internal view returns (bool) {
address consensus = CONSENSUS_CONTRACT_POSITION.getStorageAddress();
return IConsensusContract(consensus).getIsMember(addr);
}
/// @notice Called when the oracle gets a new consensus report from the HashConsensus contract.
///
/// Keep in mind that, until you call `_startProcessing`, the oracle committee is free to
/// reach consensus on another report for the same reporting frame and re-submit it using
/// this function, or lose consensus on the report and ask to discard it by calling the
/// `_handleConsensusReportDiscarded` function.
///
function _handleConsensusReport(
ConsensusReport memory report,
uint256 prevSubmittedRefSlot,
uint256 prevProcessingRefSlot
) internal virtual;
/// @notice Called when the HashConsensus contract loses consensus on a previously submitted
/// report that is not processing yet and asks to discard this report. Only called if there is
/// no new consensus report at the moment; otherwise, `_handleConsensusReport` is called instead.
///
function _handleConsensusReportDiscarded(
ConsensusReport memory report
) internal virtual {} // solhint-disable-line no-empty-blocks
/// @notice May be called by a descendant contract to check if the received data matches
/// the currently submitted consensus report. Reverts otherwise.
///
function _checkConsensusData(
uint256 refSlot,
uint256 consensusVersion,
bytes32 hash
) internal view {
ConsensusReport memory report = _storageConsensusReport().value;
if (refSlot != report.refSlot) {
revert UnexpectedRefSlot(report.refSlot, refSlot);
}
uint256 expectedConsensusVersion = CONSENSUS_VERSION_POSITION
.getStorageUint256();
if (consensusVersion != expectedConsensusVersion) {
revert UnexpectedConsensusVersion(
expectedConsensusVersion,
consensusVersion
);
}
if (hash != report.hash) {
revert UnexpectedDataHash(report.hash, hash);
}
}
/// @notice Called by a descendant contract to mark the current consensus report
/// as being processed. Returns the last ref. slot which processing was started
/// before the call.
///
/// Before this function is called, the oracle committee is free to reach consensus
/// on another report for the same reporting frame. After this function is called,
/// the consensus report for the current frame is guaranteed to remain the same.
///
function _startProcessing() internal returns (uint256) {
ConsensusReport memory report = _storageConsensusReport().value;
if (report.hash == bytes32(0)) {
revert NoConsensusReportToProcess();
}
_checkProcessingDeadline(report.processingDeadlineTime);
uint256 prevProcessingRefSlot = LAST_PROCESSING_REF_SLOT_POSITION
.getStorageUint256();
if (prevProcessingRefSlot == report.refSlot) {
revert RefSlotAlreadyProcessing();
}
LAST_PROCESSING_REF_SLOT_POSITION.setStorageUint256(report.refSlot);
emit ProcessingStarted(report.refSlot, report.hash);
return prevProcessingRefSlot;
}
function _checkProcessingDeadline(uint256 deadlineTime) internal view {
if (_getTime() > deadlineTime) {
revert ProcessingDeadlineMissed(deadlineTime);
}
}
///
/// Implementation & helpers
///
function _setConsensusVersion(uint256 version) internal {
uint256 prevVersion = CONSENSUS_VERSION_POSITION.getStorageUint256();
if (version == prevVersion) {
revert VersionCannotBeSame();
}
if (version == 0) {
revert VersionCannotBeZero();
}
CONSENSUS_VERSION_POSITION.setStorageUint256(version);
emit ConsensusVersionSet(version, prevVersion);
}
function _setConsensusContract(
address addr,
uint256 lastProcessingRefSlot
) internal {
if (addr == address(0)) {
revert AddressCannotBeZero();
}
address prevAddr = CONSENSUS_CONTRACT_POSITION.getStorageAddress();
if (addr == prevAddr) {
revert AddressCannotBeSame();
}
(, uint256 secondsPerSlot, uint256 genesisTime) = IConsensusContract(
addr
).getChainConfig();
if (secondsPerSlot != SECONDS_PER_SLOT || genesisTime != GENESIS_TIME) {
revert UnexpectedChainConfig();
}
uint256 initialRefSlot = IConsensusContract(addr).getInitialRefSlot();
if (initialRefSlot < lastProcessingRefSlot) {
revert InitialRefSlotCannotBeLessThanProcessingOne(
initialRefSlot,
lastProcessingRefSlot
);
}
CONSENSUS_CONTRACT_POSITION.setStorageAddress(addr);
emit ConsensusHashContractSet(addr, prevAddr);
}
function _checkSenderIsConsensusContract() internal view {
if (_msgSender() != CONSENSUS_CONTRACT_POSITION.getStorageAddress()) {
revert SenderIsNotTheConsensusContract();
}
}
function _getTime() internal view virtual returns (uint256) {
return block.timestamp; // solhint-disable-line not-rely-on-time
}
///
/// Storage helpers
///
struct StorageConsensusReport {
ConsensusReport value;
}
function _storageConsensusReport()
internal
pure
returns (StorageConsensusReport storage r)
{
bytes32 position = CONSENSUS_REPORT_POSITION;
assembly {
r.slot := position
}
}
}
"
},
"src/interfaces/ICSFeeDistributor.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol";
import { IStETH } from "./IStETH.sol";
pragma solidity 0.8.24;
interface ICSFeeDistributor is IAssetRecovererLib {
struct DistributionData {
/// @dev Reference slot for which the report was calculated. If the slot
/// contains a block, the state being reported should include all state
/// changes resulting from that block. The epoch containing the slot
/// should be finalized prior to calculating the report.
uint256 refSlot;
/// @notice Merkle Tree root.
bytes32 treeRoot;
/// @notice CID of the published Merkle tree.
string treeCid;
/// @notice CID of the file with log of the frame reported.
string logCid;
/// @notice Total amount of fees distributed in the report.
uint256 distributed;
/// @notice Amount of the rebate shares in the report
uint256 rebate;
}
/// @dev Emitted when fees are distributed
event OperatorFeeDistributed(
uint256 indexed nodeOperatorId,
uint256 shares
);
/// @dev Emitted when distribution data is updated
event DistributionDataUpdated(
uint256 totalClaimableShares,
bytes32 treeRoot,
string treeCid
);
/// @dev Emitted when distribution log is updated
event DistributionLogUpdated(string logCid);
/// @dev It logs how many shares were distributed in the latest report
event ModuleFeeDistributed(uint256 shares);
/// @dev Emitted when rebate is transferred
event RebateTransferred(uint256 shares);
/// @dev Emitted when rebate recipient is set
event RebateRecipientSet(address recipient);
error ZeroAccountingAddress();
error ZeroStEthAddress();
error ZeroAdminAddress();
error ZeroOracleAddress();
error ZeroRebateRecipientAddress();
error SenderIsNotAccounting();
error SenderIsNotOracle();
error InvalidReportData();
error InvalidTreeRoot();
error InvalidTreeCid();
error InvalidLogCID();
error InvalidShares();
error InvalidProof();
error FeeSharesDecrease();
error NotEnoughShares();
function RECOVERER_ROLE() external view returns (bytes32);
function STETH() external view returns (IStETH);
function ACCOUNTING() external view returns (address);
function ORACLE() external view returns (address);
function treeRoot() external view returns (bytes32);
function treeCid() external view returns (string calldata);
function logCid() external view returns (string calldata);
function distributedShares(uint256) external view returns (uint256);
function totalClaimableShares() external view returns (uint256);
function distributionDataHistoryCount() external view returns (uint256);
function rebateRecipient() external view returns (address);
/// @notice Get the initialized version of the contract
function getInitializedVersion() external view returns (uint64);
/// @notice Set address to send rebate to
/// @param _rebateRecipient Address to send rebate to
function setRebateRecipient(address _rebateRecipient) external;
/// @notice Get the Amount of stETH shares that can be distributed in favor of the Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @param cumulativeFeeShares Total Amount of stETH shares earned as fees
/// @param proof Merkle proof of the leaf
/// @return sharesToDistribute Amount of stETH shares that can be distributed
function getFeesToDistribute(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
bytes32[] calldata proof
) external view returns (uint256);
/// @notice Distribute fees to the Accounting in favor of the Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @param cumulativeFeeShares Total Amount of stETH shares earned as fees
/// @param proof Merkle proof of the leaf
/// @return sharesToDistribute Amount of stETH shares distributed
function distributeFees(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
bytes32[] calldata proof
) external returns (uint256);
/// @notice Receive the data of the Merkle tree from the Oracle contract and process it
/// @param _treeRoot Root of the Merkle tree
/// @param _treeCid an IPFS CID of the tree
/// @param _logCid an IPFS CID of the log
/// @param distributed an amount of the distributed shares
/// @param rebate an amount of the rebate shares
/// @param refSlot refSlot of the report
function processOracleReport(
bytes32 _treeRoot,
string calldata _treeCid,
string calldata _logCid,
uint256 distributed,
uint256 rebate,
uint256 refSlot
) external;
/// @notice Get the Amount of stETH shares that are pending to be distributed
/// @return pendingShares Amount shares that are pending to distribute
function pendingSharesToDistribute() external view returns (uint256);
/// @notice Get the historical record of distribution data
/// @param index Historical entry index
/// @return Historical distribution data
function getHistoricalDistributionData(
uint256 index
) external view returns (DistributionData memory);
/// @notice Get a hash of a leaf
/// @param nodeOperatorId ID of the Node Operator
/// @param shares Amount of stETH shares
/// @return Hash of the leaf
/// @dev Double hash the leaf to prevent second preimage attacks
function hashLeaf(
uint256 nodeOperatorId,
uint256 shares
) external pure returns (bytes32);
}
"
},
"src/interfaces/ICSStrikes.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { ICSModule } from "./ICSModule.sol";
import { ICSAccounting } from "./ICSAccounting.sol";
import { ICSParametersRegistry } from "./ICSParametersRegistry.sol";
import { ICSExitPenalties } from "./ICSExitPenalties.sol";
import { ICSEjector } from "./ICSEjector.sol";
interface ICSStrikes {
/// @dev Emitted when strikes data is updated
event StrikesDataUpdated(bytes32 treeRoot, string treeCid);
/// @dev Emitted when strikes is updated from non-empty to empty
event StrikesDataWiped();
event EjectorSet(address ejector);
error ZeroEjectorAddress();
error ZeroModuleAddress();
error ZeroOracleAddress();
error ZeroExitPenaltiesAddress();
error ZeroParametersRegistryAddress();
error ZeroAdminAddress();
error SenderIsNotOracle();
error ValueNotEvenlyDivisible();
error EmptyKeyStrikesList();
error ZeroMsgValue();
error InvalidReportData();
error InvalidProof();
error NotEnoughStrikesToEject();
struct KeyStrikes {
uint256 nodeOperatorId;
uint256 keyIndex;
uint256[] data;
}
function ORACLE() external view returns (address);
function MODULE() external view returns (ICSModule);
function ACCOUNTING() external view returns (ICSAccounting);
function EXIT_PENALTIES() external view returns (ICSExitPenalties);
function PARAMETERS_REGISTRY()
external
view
returns (ICSParametersRegistry);
function ejector() external view returns (ICSEjector);
function treeRoot() external view returns (bytes32);
function treeCid() external view returns (string calldata);
/// @notice Set the address of the Ejector contract
/// @param _ejector Address of the Ejector contract
function setEjector(address _ejector) external;
/// @notice Report multiple CSM keys as bad performing
/// @param keyStrikesList List of KeyStrikes structs
/// @param proof Multi-proof of the strikes
/// @param proofFlags Flags to process the multi-proof, see OZ `processMultiProof`
/// @param refundRecipient Address to send the refund to
function processBadPerformanceProof(
KeyStrikes[] calldata keyStrikesList,
bytes32[] calldata proof,
bool[] calldata proofFlags,
address refundRecipient
) external payable;
/// @notice Receive the data of the Merkle tree from the Oracle contract and process it
/// @param _treeRoot Root of the Merkle tree
/// @param _treeCid an IPFS CID of the tree
/// @dev New tree might be empty and it is valid value because of `strikesLifetime`
function processOracleReport(
bytes32 _treeRoot,
string calldata _treeCid
) external;
/// @notice Check the contract accepts the provided multi-proof
/// @param keyStrikesList List of KeyStrikes structs
/// @param proof Multi-proof of the strikes
/// @param proofFlags Flags to process the multi-proof, see OZ `processMultiProof`
/// @return bool True if proof is accepted
function verifyProof(
KeyStrikes[] calldata keyStrikesList,
bytes[] memory pubkeys,
bytes32[] calldata proof,
bool[] calldata proofFlags
) external view returns (bool);
/// @notice Get a hash of a leaf a tree of strikes
/// @param keyStrikes KeyStrikes struct
/// @param pubkey Public key
/// @return Hash of the leaf
/// @dev Double hash the leaf to prevent second pre-image attacks
function hashLeaf(
KeyStrikes calldata keyStrikes,
bytes calldata pubkey
) external pure returns (bytes32);
/// @notice Returns the initialized version of the contract
function getInitializedVersion() external view returns (uint64);
}
"
},
"src/interfaces/ICSFeeOracle.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol";
import { ICSFeeDistributor } from "./ICSFeeDistributor.sol";
import { ICSStrikes } from "./ICSStrikes.sol";
interface ICSFeeOracle is IAssetRecovererLib {
struct ReportData {
/// @dev Version of the oracle consensus rules. Current version expected
/// by the oracle can be obtained by calling getConsensusVersion().
uint256 consensusVersion;
/// @dev Reference slot for which the report was calculated. If the slot
/// contains a block, the state being reported should include all state
/// changes resulting from that block. The epoch containing the slot
/// should be finalized prior to calculating the report.
uint256 refSlot;
/// @notice Merkle Tree root.
bytes32 treeRoot;
/// @notice CID of the published Merkle tree.
string treeCid;
/// @notice CID of the file with log of the frame reported.
string logCid;
/// @notice Total amount of fees distributed in the report.
uint256 distributed;
/// @notice Amount of the rebate shares in the report
uint256 rebate;
/// @notice Merkle Tree root of the strikes.
bytes32 strikesTreeRoot;
/// @notice CID of the published Merkle tree of the strikes.
string strikesTreeCid;
}
error ZeroAdminAddress();
error ZeroFeeDistributorAddress();
error ZeroStrikesAddress();
error SenderNotAllowed();
function SUBMIT_DATA_ROLE() external view returns (bytes32);
function PAUSE_ROLE() external view returns (bytes32);
function RESUME_ROLE() external view returns (bytes32);
function RECOVERER_ROLE() external view returns (bytes32);
function FEE_DISTRIBUTOR() external view returns (ICSFeeDistributor);
function STRIKES() external view returns (ICSStrikes);
/// @notice Submit the data for a committee report
/// @param data Data for a committee report
/// @param contractVersion Version of the oracle consensus rules
function submitReportData(
ReportData calldata data,
uint256 contractVersion
) external;
/// @notice Resume accepting oracle reports
function resume() external;
/// @notice Pause accepting oracle reports for a `duration` seconds
/// @param duration Duration of the pause in seconds
function pauseFor(uint256 duration) external;
}
"
},
"src/lib/AssetRecovererLib.sol": {
"content": "// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ILido } from "../interfaces/ILido.sol";
interface IAssetRecovererLib {
event EtherRecovered(address indexed recipient, uint256 amount);
event ERC20Recovered(
address indexed token,
address indexed recipient,
uint256 amount
);
event StETHSharesRecovered(address indexed recipient, uint256 shares);
event ERC721Recovered(
address indexed token,
uint256 tokenId,
address indexed recipient
);
event ERC1155Recovered(
address indexed token,
uint256 tokenId,
address indexed recipient,
uint256 amount
);
error FailedToSendEther();
error NotAllowedToRecover();
}
/*
* @title AssetRecovererLib
* @dev Library providing mechanisms for recovering various asset types (ETH, ERC20, ERC721, ERC1155).
* This library is designed to be used by a contract that implements the AssetRecoverer interface.
*/
library AssetRecovererLib {
using SafeERC20 for IERC20;
/**
* @dev Allows the sender to recover Ether held by the contract.
* Emits an EtherRecovered event upon success.
*/
function recoverEther() external {
uint256 amount = address(this).balance;
(bool success, ) = msg.sender.call{ value: amount }("");
if (!success) {
revert IAssetRecovererLib.FailedToSendEther();
}
emit IAssetRecovererLib.EtherRecovered(msg.sender, amount);
}
/**
* @dev Allows the 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.
*/
function recoverERC20(address token, uint256 amount) external {
IERC20(token).safeTransfer(msg.sender, amount);
emit IAssetRecovererLib.ERC20Recovered(token, msg.sender, amount);
}
/**
* @dev Allows the sender to recover stETH shares held by the contract.
* The use of a separate method for stETH is to avoid rounding problems when converting shares to stETH.
* @param lido The address of the Lido contract.
* @param shares The amount of stETH shares to recover.
* Emits an StETHSharesRecovered event upon success.
*/
function recoverStETHShares(address lido, uint256 shares) external {
ILido(lido).transferShares(msg.sender, shares);
emit IAssetRecovererLib.StETHSharesRecovered(msg.sender, shares);
}
/**
* @dev Allows the 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 {
IERC721(token).safeTransferFrom(address(this), msg.sender, tokenId);
emit IAssetRecovererLib.ERC721Recovered(token, tokenId, msg.sender);
}
/**
* @dev Allows the 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 {
uint256 amount = IERC1155(token).balanceOf(address(this), tokenId);
IERC1155(token).safeTransferFrom({
from: address(this),
to: msg.sender,
id: tokenId,
value: amount,
data: ""
});
emit IAssetRecovererLib.ERC1155Recovered(
token,
tokenId,
msg.sender,
amount
);
}
}
"
},
"src/lib/UnstructuredStorage.sol": {
"content": "/*
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.24;
/**
* @notice Aragon Unstructured Storage library
*/
library UnstructuredStorage {
function setStorageAddress(bytes32 position, address data) internal {
assembly {
sstore(position, data)
}
}
function setStorageUint256(bytes32 position, uint256 data) internal {
assembly {
sstore(position, data)
}
}
function getStorageAddress(
bytes32 position
) internal view returns (address data) {
assembly {
data := sload(position)
}
}
function getStorageUint256(
bytes32 position
) internal view returns (uint256 data) {
assembly {
data := sload(position)
}
}
}
"
},
"node_modules/@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.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 "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable
struct AccessControlEnumerableStorage {
mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000;
function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) {
assembly {
$.slot := AccessControlEnumerableStorageLocation
}
}
function __AccessControlEnumerable_init() internal onlyInitializing {
}
function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
}
/**
* @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) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
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) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
return $._roleMembers[role].length();
}
/**
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
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) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
bool revoked = super._revokeRole(role, account);
if (revoked) {
$._roleMembers[role].remove(account);
}
return revoked;
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/math/SafeCast.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if
Submitted on: 2025-09-17 15:49:20
Comments
Log in to comment.
No comments yet.