Holesky Testnet

Contract

0x3B377e3ac2CC844d8d27B8930F6a3035d3A3Fc5B

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
0x608060405292122023-12-15 10:00:12304 days ago1702634412IN
 Contract Creation
0 ETH0.001923351

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x4a32c40b...f204aD382
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
RedeemManagerV1

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 10 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 25 : RedeemManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IAllowlist.1.sol";
import "./interfaces/IRiver.1.sol";
import "./interfaces/IRedeemManager.1.sol";
import "./libraries/LibAllowlistMasks.sol";
import "./libraries/LibUint256.sol";
import "./Initializable.sol";

import "./state/shared/RiverAddress.sol";
import "./state/redeemManager/RedeemQueue.sol";
import "./state/redeemManager/WithdrawalStack.sol";
import "./state/redeemManager/BufferedExceedingEth.sol";
import "./state/redeemManager/RedeemDemand.sol";

/// @title Redeem Manager (v1)
/// @author Kiln
/// @notice This contract handles the redeem requests of all users
contract RedeemManagerV1 is Initializable, IRedeemManagerV1 {
    /// @notice Value returned when resolving a redeem request that is unsatisfied
    int64 internal constant RESOLVE_UNSATISFIED = -1;
    /// @notice Value returned when resolving a redeem request that is out of bounds
    int64 internal constant RESOLVE_OUT_OF_BOUNDS = -2;
    /// @notice Value returned when resolving a redeem request that is already claimed
    int64 internal constant RESOLVE_FULLY_CLAIMED = -3;

    /// @notice Status value returned when fully claiming a redeem request
    uint8 internal constant CLAIM_FULLY_CLAIMED = 0;
    /// @notice Status value returned when partially claiming a redeem request
    uint8 internal constant CLAIM_PARTIALLY_CLAIMED = 1;
    /// @notice Status value returned when a redeem request is already claimed and skipped during a claim
    uint8 internal constant CLAIM_SKIPPED = 2;

    modifier onlyRiver() {
        if (msg.sender != RiverAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    modifier onlyRedeemerOrRiver() {
        {
            IRiverV1 river = _castedRiver();
            if (msg.sender != address(river)) {
                IAllowlistV1(river.getAllowlist()).onlyAllowed(msg.sender, LibAllowlistMasks.REDEEM_MASK);
            }
        }
        _;
    }

    modifier onlyRedeemer() {
        {
            IRiverV1 river = _castedRiver();
            IAllowlistV1(river.getAllowlist()).onlyAllowed(msg.sender, LibAllowlistMasks.REDEEM_MASK);
        }
        _;
    }

    /// @inheritdoc IRedeemManagerV1
    function initializeRedeemManagerV1(address _river) external init(0) {
        RiverAddress.set(_river);
        emit SetRiver(_river);
    }

    /// @inheritdoc IRedeemManagerV1
    function getRiver() external view returns (address) {
        return RiverAddress.get();
    }

    /// @inheritdoc IRedeemManagerV1
    function getRedeemRequestCount() external view returns (uint256) {
        return RedeemQueue.get().length;
    }

    /// @inheritdoc IRedeemManagerV1
    function getRedeemRequestDetails(uint32 _redeemRequestId)
        external
        view
        returns (RedeemQueue.RedeemRequest memory)
    {
        return RedeemQueue.get()[_redeemRequestId];
    }

    /// @inheritdoc IRedeemManagerV1
    function getWithdrawalEventCount() external view returns (uint256) {
        return WithdrawalStack.get().length;
    }

    /// @inheritdoc IRedeemManagerV1
    function getWithdrawalEventDetails(uint32 _withdrawalEventId)
        external
        view
        returns (WithdrawalStack.WithdrawalEvent memory)
    {
        return WithdrawalStack.get()[_withdrawalEventId];
    }

    /// @inheritdoc IRedeemManagerV1
    function getBufferedExceedingEth() external view returns (uint256) {
        return BufferedExceedingEth.get();
    }

    /// @inheritdoc IRedeemManagerV1
    function getRedeemDemand() external view returns (uint256) {
        return RedeemDemand.get();
    }

    /// @inheritdoc IRedeemManagerV1
    function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
        external
        view
        returns (int64[] memory withdrawalEventIds)
    {
        withdrawalEventIds = new int64[](_redeemRequestIds.length);
        WithdrawalStack.WithdrawalEvent memory lastWithdrawalEvent;
        WithdrawalStack.WithdrawalEvent[] storage withdrawalEvents = WithdrawalStack.get();
        uint256 withdrawalEventsLength = withdrawalEvents.length;
        if (withdrawalEventsLength > 0) {
            lastWithdrawalEvent = withdrawalEvents[withdrawalEventsLength - 1];
        }
        for (uint256 idx = 0; idx < _redeemRequestIds.length; ++idx) {
            withdrawalEventIds[idx] = _resolveRedeemRequestId(_redeemRequestIds[idx], lastWithdrawalEvent);
        }
    }

    /// @inheritdoc IRedeemManagerV1
    function requestRedeem(uint256 _lsETHAmount, address _recipient)
        external
        onlyRedeemerOrRiver
        returns (uint32 redeemRequestId)
    {
        return _requestRedeem(_lsETHAmount, _recipient);
    }

    /// @inheritdoc IRedeemManagerV1
    function requestRedeem(uint256 _lsETHAmount) external onlyRedeemer returns (uint32 redeemRequestId) {
        return _requestRedeem(_lsETHAmount, msg.sender);
    }

    /// @inheritdoc IRedeemManagerV1
    function claimRedeemRequests(
        uint32[] calldata redeemRequestIds,
        uint32[] calldata withdrawalEventIds,
        bool skipAlreadyClaimed,
        uint16 _depth
    ) external returns (uint8[] memory claimStatuses) {
        return _claimRedeemRequests(redeemRequestIds, withdrawalEventIds, skipAlreadyClaimed, _depth);
    }

    /// @inheritdoc IRedeemManagerV1
    function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
        external
        returns (uint8[] memory claimStatuses)
    {
        return _claimRedeemRequests(_redeemRequestIds, _withdrawalEventIds, true, type(uint16).max);
    }

    /// @inheritdoc IRedeemManagerV1
    function reportWithdraw(uint256 _lsETHWithdrawable) external payable onlyRiver {
        uint256 redeemDemand = RedeemDemand.get();
        if (_lsETHWithdrawable > redeemDemand) {
            revert WithdrawalExceedsRedeemDemand(_lsETHWithdrawable, redeemDemand);
        }
        WithdrawalStack.WithdrawalEvent[] storage withdrawalEvents = WithdrawalStack.get();
        uint32 withdrawalEventId = uint32(withdrawalEvents.length);
        uint256 height = 0;
        uint256 msgValue = msg.value;
        if (withdrawalEventId != 0) {
            WithdrawalStack.WithdrawalEvent memory previousWithdrawalEvent = withdrawalEvents[withdrawalEventId - 1];
            height = previousWithdrawalEvent.height + previousWithdrawalEvent.amount;
        }
        withdrawalEvents.push(
            WithdrawalStack.WithdrawalEvent({height: height, amount: _lsETHWithdrawable, withdrawnEth: msgValue})
        );
        _setRedeemDemand(redeemDemand - _lsETHWithdrawable);
        emit ReportedWithdrawal(height, _lsETHWithdrawable, msgValue, withdrawalEventId);
    }

    /// @inheritdoc IRedeemManagerV1
    function pullExceedingEth(uint256 _max) external onlyRiver {
        uint256 amountToSend = LibUint256.min(BufferedExceedingEth.get(), _max);
        if (amountToSend > 0) {
            BufferedExceedingEth.set(BufferedExceedingEth.get() - amountToSend);
            _castedRiver().sendRedeemManagerExceedingFunds{value: amountToSend}();
        }
    }

    /// @notice Internal utility to load and cast the River address
    /// @return The casted river address
    function _castedRiver() internal view returns (IRiverV1) {
        return IRiverV1(payable(RiverAddress.get()));
    }

    /// @notice Internal utility to verify if a redeem request and a withdrawal event are matching
    /// @param _redeemRequest The loaded redeem request
    /// @param _withdrawalEvent The load withdrawal event
    /// @return True if matching
    function _isMatch(
        RedeemQueue.RedeemRequest memory _redeemRequest,
        WithdrawalStack.WithdrawalEvent memory _withdrawalEvent
    ) internal pure returns (bool) {
        return (
            _redeemRequest.height < _withdrawalEvent.height + _withdrawalEvent.amount
                && _redeemRequest.height >= _withdrawalEvent.height
        );
    }

    /// @notice Internal utility to perform a dichotomic search of the withdrawal event to use to claim the redeem request
    /// @param _redeemRequest The redeem request to resolve
    /// @return The matching withdrawal event
    function _performDichotomicResolution(RedeemQueue.RedeemRequest memory _redeemRequest)
        internal
        view
        returns (int64)
    {
        WithdrawalStack.WithdrawalEvent[] storage withdrawalEvents = WithdrawalStack.get();

        int64 max = int64(int256(WithdrawalStack.get().length - 1));

        if (_isMatch(_redeemRequest, withdrawalEvents[uint64(max)])) {
            return max;
        }

        int64 min = 0;

        if (_isMatch(_redeemRequest, withdrawalEvents[uint64(min)])) {
            return min;
        }

        // we start a dichotomic search between min and max
        while (min != max) {
            int64 mid = (min + max) / 2;

            // we identify and verify that the middle element is not matching
            WithdrawalStack.WithdrawalEvent memory midWithdrawalEvent = withdrawalEvents[uint64(mid)];
            if (_isMatch(_redeemRequest, midWithdrawalEvent)) {
                return mid;
            }

            // depending on the position of the middle element, we update max or min to get our min max range
            // closer to our redeem request position
            if (_redeemRequest.height < midWithdrawalEvent.height) {
                max = mid;
            } else {
                min = mid;
            }
        }
        return min;
    }

    /// @notice Internal utility to resolve a redeem request and retrieve its satisfying withdrawal event id, or identify possible errors
    /// @param _redeemRequestId The redeem request id
    /// @param _lastWithdrawalEvent The last withdrawal event loaded in memory
    /// @return withdrawalEventId The id of the withdrawal event matching the redeem request or error code
    function _resolveRedeemRequestId(
        uint32 _redeemRequestId,
        WithdrawalStack.WithdrawalEvent memory _lastWithdrawalEvent
    ) internal view returns (int64 withdrawalEventId) {
        RedeemQueue.RedeemRequest[] storage redeemRequests = RedeemQueue.get();
        // if the redeem request id is >= than the size of requests, we know it's out of bounds and doesn't exist
        if (_redeemRequestId >= redeemRequests.length) {
            return RESOLVE_OUT_OF_BOUNDS;
        }
        RedeemQueue.RedeemRequest memory redeemRequest = redeemRequests[_redeemRequestId];
        // if the redeem request remaining amount is 0, we know that the request has been entirely claimed
        if (redeemRequest.amount == 0) {
            return RESOLVE_FULLY_CLAIMED;
        }
        // if there are no existing withdrawal events or if the height of the redeem request is higher than the height and
        // amount of the last withdrawal element, we know that the redeem request is not yet satisfied
        if (
            WithdrawalStack.get().length == 0
                || (_lastWithdrawalEvent.height + _lastWithdrawalEvent.amount) <= redeemRequest.height
        ) {
            return RESOLVE_UNSATISFIED;
        }
        // we know for sure that the redeem request has funds yet to be claimed and there is a withdrawal event we need to identify
        // that would allow the user to claim the redeem request
        return _performDichotomicResolution(redeemRequest);
    }

    /// @notice Perform a new redeem request for the specified recipient
    /// @param _lsETHAmount The amount of LsETH to redeem
    /// @param _recipient The recipient owning the request
    /// @return redeemRequestId The id of the newly created redeem request
    function _requestRedeem(uint256 _lsETHAmount, address _recipient) internal returns (uint32 redeemRequestId) {
        LibSanitize._notZeroAddress(_recipient);
        if (_lsETHAmount == 0) {
            revert InvalidZeroAmount();
        }
        if (!_castedRiver().transferFrom(msg.sender, address(this), _lsETHAmount)) {
            revert TransferError();
        }
        RedeemQueue.RedeemRequest[] storage redeemRequests = RedeemQueue.get();
        redeemRequestId = uint32(redeemRequests.length);
        uint256 height = 0;
        if (redeemRequestId != 0) {
            RedeemQueue.RedeemRequest memory previousRedeemRequest = redeemRequests[redeemRequestId - 1];
            height = previousRedeemRequest.height + previousRedeemRequest.amount;
        }

        uint256 maxRedeemableEth = _castedRiver().underlyingBalanceFromShares(_lsETHAmount);

        redeemRequests.push(
            RedeemQueue.RedeemRequest({
                height: height,
                amount: _lsETHAmount,
                owner: _recipient,
                maxRedeemableEth: maxRedeemableEth
            })
        );

        _setRedeemDemand(RedeemDemand.get() + _lsETHAmount);

        emit RequestedRedeem(_recipient, height, _lsETHAmount, maxRedeemableEth, redeemRequestId);
    }

    /// @notice Internal structure used to optimize stack usage in _claimRedeemRequest
    struct ClaimRedeemRequestParameters {
        /// @custom:attribute The id of the redeem request to claim
        uint32 redeemRequestId;
        /// @custom:attribute The structure of the redeem request to claim
        RedeemQueue.RedeemRequest redeemRequest;
        /// @custom:attribute The id of the withdrawal event to use to claim the redeem request
        uint32 withdrawalEventId;
        /// @custom:attribute The structure of the withdrawal event to use to claim the redeem request
        WithdrawalStack.WithdrawalEvent withdrawalEvent;
        /// @custom:attribute The count of withdrawal events
        uint32 withdrawalEventCount;
        /// @custom:attribute The current depth of the recursive call
        uint16 depth;
        /// @custom:attribute The amount of LsETH redeemed/matched, needs to be reset to 0 for each call/before calling the recursive function
        uint256 lsETHAmount;
        /// @custom:attribute The amount of eth redeemed/matched, needs to be rest to 0 for each call/before calling the recursive function
        uint256 ethAmount;
    }

    /// @notice Internal structure used to optimize stack usage in _claimRedeemRequest
    struct ClaimRedeemRequestInternalVariables {
        /// @custom:attribute The eth amount claimed by the user
        uint256 ethAmount;
        /// @custom:attribute The amount of LsETH matched during this step
        uint256 matchingAmount;
        /// @custom:attribute The amount of eth redirected to the exceeding eth buffer
        uint256 exceedingEthAmount;
    }

    /// @notice Internal utility to save a redeem request to storage
    /// @param _params The parameters of the claim redeem request call
    function _saveRedeemRequest(ClaimRedeemRequestParameters memory _params) internal {
        RedeemQueue.RedeemRequest[] storage redeemRequests = RedeemQueue.get();
        redeemRequests[_params.redeemRequestId].height = _params.redeemRequest.height;
        redeemRequests[_params.redeemRequestId].amount = _params.redeemRequest.amount;
        redeemRequests[_params.redeemRequestId].maxRedeemableEth = _params.redeemRequest.maxRedeemableEth;
    }

    /// @notice Internal utility to claim a redeem request if possible
    /// @dev Will call itself recursively if the redeem requests overflows its matching withdrawal event
    /// @param _params The parameters of the claim redeem request call
    function _claimRedeemRequest(ClaimRedeemRequestParameters memory _params) internal {
        ClaimRedeemRequestInternalVariables memory vars;
        {
            uint256 withdrawalEventEndPosition = _params.withdrawalEvent.height + _params.withdrawalEvent.amount;

            // it can occur that the redeem request is overlapping the provided withdrawal event
            // the amount that is matched in the withdrawal event is adapted depending on this
            vars.matchingAmount =
                LibUint256.min(_params.redeemRequest.amount, withdrawalEventEndPosition - _params.redeemRequest.height);
            // we can now compute the equivalent eth amount based on the withdrawal event details
            vars.ethAmount =
                (vars.matchingAmount * _params.withdrawalEvent.withdrawnEth) / _params.withdrawalEvent.amount;

            // as each request has a maximum withdrawable amount, we verify that the eth amount is not exceeding this amount, pro rata
            // the amount that is matched
            uint256 maxRedeemableEthAmount =
                (vars.matchingAmount * _params.redeemRequest.maxRedeemableEth) / _params.redeemRequest.amount;

            if (maxRedeemableEthAmount < vars.ethAmount) {
                vars.exceedingEthAmount = vars.ethAmount - maxRedeemableEthAmount;
                BufferedExceedingEth.set(BufferedExceedingEth.get() + vars.exceedingEthAmount);
                vars.ethAmount = maxRedeemableEthAmount;
            }

            // height and amount are updated to reflect the amount that was matched.
            // we will always keep this invariant true oldRequest.height + oldRequest.amount == newRequest.height + newRequest.amount
            // this also means that if the request wasn't entirely matched, it will now be automatically be assigned to the next
            // withdrawal event in the queue, because height is updated based on the amount matched and is now equal to the height
            // of the next withdrawal event
            // the end position of a redeem request (height + amount) is an invariant that never changes throughout the lifetime of a request
            // this end position is used to define the starting position of the next redeem request
            _params.redeemRequest.height += vars.matchingAmount;
            _params.redeemRequest.amount -= vars.matchingAmount;
            _params.redeemRequest.maxRedeemableEth -= vars.ethAmount;

            _params.lsETHAmount += vars.matchingAmount;
            _params.ethAmount += vars.ethAmount;

            // this event signals that an amount has been matched from a redeem request on a withdrawal event
            // this event can be triggered several times for the same redeem request, depending on its size and
            // how many withdrawal events it overlaps.
            emit SatisfiedRedeemRequest(
                _params.redeemRequestId,
                _params.withdrawalEventId,
                vars.matchingAmount,
                vars.ethAmount,
                _params.redeemRequest.amount,
                vars.exceedingEthAmount
            );
        }

        // in the case where we haven't claimed all the redeem request AND that there are other withdrawal events
        // available next in the stack, we load the next withdrawal event and call this method recursively
        // also we stop the claim process if the claim depth is about to be 0
        if (
            _params.redeemRequest.amount > 0 && _params.withdrawalEventId + 1 < _params.withdrawalEventCount
                && _params.depth > 0
        ) {
            WithdrawalStack.WithdrawalEvent[] storage withdrawalEvents = WithdrawalStack.get();

            ++_params.withdrawalEventId;
            _params.withdrawalEvent = withdrawalEvents[_params.withdrawalEventId];
            --_params.depth;

            _claimRedeemRequest(_params);
        } else {
            // if we end up here, we either claimed everything or we reached the end of the withdrawal event stack
            // in this case we save the current redeem request state to storage and return the status according to the
            // remaining claimable amount on the redeem request
            _saveRedeemRequest(_params);
        }
    }

    /// @notice Internal utility to claim several redeem requests at once
    /// @param _redeemRequestIds The list of redeem requests to claim
    /// @param _withdrawalEventIds The list of withdrawal events to use for each redeem request. Should have the same length.
    /// @param _skipAlreadyClaimed True if the system should skip redeem requests already claimed, otherwise will revert
    /// @param _depth The depth of the recursion to use when claiming a redeem request
    /// @return claimStatuses The claim statuses for each redeem request
    function _claimRedeemRequests(
        uint32[] calldata _redeemRequestIds,
        uint32[] calldata _withdrawalEventIds,
        bool _skipAlreadyClaimed,
        uint16 _depth
    ) internal returns (uint8[] memory claimStatuses) {
        uint256 redeemRequestIdsLength = _redeemRequestIds.length;
        if (redeemRequestIdsLength != _withdrawalEventIds.length) {
            revert IncompatibleArrayLengths();
        }
        claimStatuses = new uint8[](redeemRequestIdsLength);

        RedeemQueue.RedeemRequest[] storage redeemRequests = RedeemQueue.get();
        WithdrawalStack.WithdrawalEvent[] storage withdrawalEvents = WithdrawalStack.get();

        ClaimRedeemRequestParameters memory params;
        params.withdrawalEventCount = uint32(withdrawalEvents.length);
        uint32 redeemRequestCount = uint32(redeemRequests.length);

        for (uint256 idx = 0; idx < redeemRequestIdsLength;) {
            // both ids are loaded into params
            params.redeemRequestId = _redeemRequestIds[idx];
            params.withdrawalEventId = _withdrawalEventIds[idx];

            // we start by checking that the id is not out of bounds for the redeem requests
            if (params.redeemRequestId >= redeemRequestCount) {
                revert RedeemRequestOutOfBounds(params.redeemRequestId);
            }

            // we check that the withdrawal event id is not out of bounds
            if (params.withdrawalEventId >= params.withdrawalEventCount) {
                revert WithdrawalEventOutOfBounds(params.withdrawalEventId);
            }

            // we load the redeem request in memory
            params.redeemRequest = redeemRequests[_redeemRequestIds[idx]];

            // we check that the redeem request is not already claimed
            if (params.redeemRequest.amount == 0) {
                if (_skipAlreadyClaimed) {
                    claimStatuses[idx] = CLAIM_SKIPPED;
                    unchecked {
                        ++idx;
                    }
                    continue;
                }
                revert RedeemRequestAlreadyClaimed(params.redeemRequestId);
            }

            // we load the withdrawal event in memory
            params.withdrawalEvent = withdrawalEvents[_withdrawalEventIds[idx]];

            // now that both entities are loaded in memory, we verify that they indeed match, otherwise we revert
            if (!_isMatch(params.redeemRequest, params.withdrawalEvent)) {
                revert DoesNotMatch(params.redeemRequestId, params.withdrawalEventId);
            }

            params.depth = _depth;
            params.ethAmount = 0;
            params.lsETHAmount = 0;

            _claimRedeemRequest(params);

            claimStatuses[idx] = params.redeemRequest.amount == 0 ? CLAIM_FULLY_CLAIMED : CLAIM_PARTIALLY_CLAIMED;

            {
                (bool success, bytes memory rdata) = params.redeemRequest.owner.call{value: params.ethAmount}("");
                if (!success) {
                    revert ClaimRedeemFailed(params.redeemRequest.owner, rdata);
                }
            }
            emit ClaimedRedeemRequest(
                _redeemRequestIds[idx],
                params.redeemRequest.owner,
                params.ethAmount,
                params.lsETHAmount,
                params.redeemRequest.amount
            );

            unchecked {
                ++idx;
            }
        }
    }

    /// @notice Internal utility to set the redeem demand
    /// @param _newValue The new value to set
    function _setRedeemDemand(uint256 _newValue) internal {
        emit SetRedeemDemand(RedeemDemand.get(), _newValue);
        RedeemDemand.set(_newValue);
    }
}

File 2 of 25 : Initializable.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./state/shared/Version.sol";

/// @title Initializable
/// @author Kiln
/// @notice This contract ensures that initializers are called only once per version
contract Initializable {
    /// @notice Disable initialization on implementations
    constructor() {
        Version.set(type(uint256).max);
        emit Initialize(type(uint256).max, msg.data);
    }

    /// @notice An error occured during the initialization
    /// @param version The version that was attempting to be initialized
    /// @param expectedVersion The version that was expected
    error InvalidInitialization(uint256 version, uint256 expectedVersion);

    /// @notice Emitted when the contract is properly initialized
    /// @param version New version of the contracts
    /// @param cdata Complete calldata that was used during the initialization
    event Initialize(uint256 version, bytes cdata);

    /// @notice Use this modifier on initializers along with a hard-coded version number
    /// @param _version Version to initialize
    modifier init(uint256 _version) {
        if (_version != Version.get()) {
            revert InvalidInitialization(_version, Version.get());
        }
        Version.set(_version + 1); // prevents reentrency on the called method
        _;
        emit Initialize(_version, msg.data);
    }
}

File 3 of 25 : IConsensusLayerDepositManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Consensys Layer Deposit Manager Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the interactions with the official deposit contract
interface IConsensusLayerDepositManagerV1 {
    /// @notice The stored deposit contract address changed
    /// @param depositContract Address of the deposit contract
    event SetDepositContractAddress(address indexed depositContract);

    /// @notice The stored withdrawal credentials changed
    /// @param withdrawalCredentials The withdrawal credentials to use for deposits
    event SetWithdrawalCredentials(bytes32 withdrawalCredentials);

    /// @notice Emitted when the deposited validator count is updated
    /// @param oldDepositedValidatorCount The old deposited validator count value
    /// @param newDepositedValidatorCount The new deposited validator count value
    event SetDepositedValidatorCount(uint256 oldDepositedValidatorCount, uint256 newDepositedValidatorCount);

    /// @notice Not enough funds to deposit one validator
    error NotEnoughFunds();

    /// @notice The length of the BLS Public key is invalid during deposit
    error InconsistentPublicKeys();

    /// @notice The length of the BLS Signature is invalid during deposit
    error InconsistentSignatures();

    /// @notice The internal key retrieval returned no keys
    error NoAvailableValidatorKeys();

    /// @notice The received count of public keys to deposit is invalid
    error InvalidPublicKeyCount();

    /// @notice The received count of signatures to deposit is invalid
    error InvalidSignatureCount();

    /// @notice The withdrawal credentials value is null
    error InvalidWithdrawalCredentials();

    /// @notice An error occured during the deposit
    error ErrorOnDeposit();

    /// @notice Returns the amount of ETH not yet committed for deposit
    /// @return The amount of ETH not yet committed for deposit
    function getBalanceToDeposit() external view returns (uint256);

    /// @notice Returns the amount of ETH committed for deposit
    /// @return The amount of ETH committed for deposit
    function getCommittedBalance() external view returns (uint256);

    /// @notice Retrieve the withdrawal credentials
    /// @return The withdrawal credentials
    function getWithdrawalCredentials() external view returns (bytes32);

    /// @notice Get the deposited validator count (the count of deposits made by the contract)
    /// @return The deposited validator count
    function getDepositedValidatorCount() external view returns (uint256);

    /// @notice Deposits current balance to the Consensus Layer by batches of 32 ETH
    /// @param _maxCount The maximum amount of validator keys to fund
    function depositToConsensusLayer(uint256 _maxCount) external;
}

File 4 of 25 : IOracleManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../state/river/CLSpec.sol";
import "../../state/river/ReportBounds.sol";

/// @title Oracle Manager (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the inputs provided by the oracle
interface IOracleManagerV1 {
    /// @notice The stored oracle address changed
    /// @param oracleAddress The new oracle address
    event SetOracle(address indexed oracleAddress);

    /// @notice The consensus layer data provided by the oracle has been updated
    /// @param validatorCount The new count of validators running on the consensus layer
    /// @param validatorTotalBalance The new total balance sum of all validators
    /// @param roundId Round identifier
    event ConsensusLayerDataUpdate(uint256 validatorCount, uint256 validatorTotalBalance, bytes32 roundId);

    /// @notice The Consensus Layer Spec is changed
    /// @param epochsPerFrame The number of epochs inside a frame
    /// @param slotsPerEpoch The number of slots inside an epoch
    /// @param secondsPerSlot The number of seconds inside a slot
    /// @param genesisTime The genesis timestamp
    /// @param epochsToAssumedFinality The number of epochs before an epoch is considered final
    event SetSpec(
        uint64 epochsPerFrame,
        uint64 slotsPerEpoch,
        uint64 secondsPerSlot,
        uint64 genesisTime,
        uint64 epochsToAssumedFinality
    );

    /// @notice The Report Bounds are changed
    /// @param annualAprUpperBound The reporting upper bound
    /// @param relativeLowerBound The reporting lower bound
    event SetBounds(uint256 annualAprUpperBound, uint256 relativeLowerBound);

    /// @notice The provided report has beend processed
    /// @param report The report that was provided
    /// @param trace The trace structure providing more insights on internals
    event ProcessedConsensusLayerReport(
        IOracleManagerV1.ConsensusLayerReport report, ConsensusLayerDataReportingTrace trace
    );

    /// @notice The reported validator count is invalid
    /// @param providedValidatorCount The received validator count value
    /// @param depositedValidatorCount The number of deposits performed by the system
    /// @param lastReportedValidatorCount The last reported validator count
    error InvalidValidatorCountReport(
        uint256 providedValidatorCount, uint256 depositedValidatorCount, uint256 lastReportedValidatorCount
    );

    /// @notice Thrown when an invalid epoch was reported
    /// @param epoch Invalid epoch
    error InvalidEpoch(uint256 epoch);

    /// @notice The balance increase is higher than the maximum allowed by the upper bound
    /// @param prevTotalEthIncludingExited The previous total balance, including all exited balance
    /// @param postTotalEthIncludingExited The post-report total balance, including all exited balance
    /// @param timeElapsed The time in seconds since last report
    /// @param annualAprUpperBound The upper bound value that was used
    error TotalValidatorBalanceIncreaseOutOfBound(
        uint256 prevTotalEthIncludingExited,
        uint256 postTotalEthIncludingExited,
        uint256 timeElapsed,
        uint256 annualAprUpperBound
    );

    /// @notice The balance decrease is higher than the maximum allowed by the lower bound
    /// @param prevTotalEthIncludingExited The previous total balance, including all exited balance
    /// @param postTotalEthIncludingExited The post-report total balance, including all exited balance
    /// @param timeElapsed The time in seconds since last report
    /// @param relativeLowerBound The lower bound value that was used
    error TotalValidatorBalanceDecreaseOutOfBound(
        uint256 prevTotalEthIncludingExited,
        uint256 postTotalEthIncludingExited,
        uint256 timeElapsed,
        uint256 relativeLowerBound
    );

    /// @notice The total exited balance decreased
    /// @param currentValidatorsExitedBalance The current exited balance
    /// @param newValidatorsExitedBalance The new exited balance
    error InvalidDecreasingValidatorsExitedBalance(
        uint256 currentValidatorsExitedBalance, uint256 newValidatorsExitedBalance
    );

    /// @notice The total skimmed balance decreased
    /// @param currentValidatorsSkimmedBalance The current exited balance
    /// @param newValidatorsSkimmedBalance The new exited balance
    error InvalidDecreasingValidatorsSkimmedBalance(
        uint256 currentValidatorsSkimmedBalance, uint256 newValidatorsSkimmedBalance
    );

    /// @notice Trace structure emitted via logs during reporting
    struct ConsensusLayerDataReportingTrace {
        uint256 rewards;
        uint256 pulledELFees;
        uint256 pulledRedeemManagerExceedingEthBuffer;
        uint256 pulledCoverageFunds;
    }

    /// @notice The format of the oracle report
    struct ConsensusLayerReport {
        // this is the epoch at which the report was performed
        // data should be fetched up to the state of this epoch by the oracles
        uint256 epoch;
        // the sum of all the validator balances on the consensus layer
        // when a validator enters the exit queue, the validator is considered stopped, its balance is accounted in both validatorsExitingBalance and validatorsBalance
        // when a validator leaves the exit queue and the funds are sweeped onto the execution layer, the balance is only accounted in validatorsExitedBalance and not in validatorsBalance
        // this value can decrease between reports
        uint256 validatorsBalance;
        // the sum of all the skimmings performed on the validators
        // these values can be found in the execution layer block bodies under the withdrawals field
        // a withdrawal is considered skimming if
        // - the epoch at which it happened is < validator.withdrawableEpoch
        // - the epoch at which it happened is >= validator.withdrawableEpoch and in that case we only account for what would be above 32 eth as skimming
        // this value cannot decrease over reports
        uint256 validatorsSkimmedBalance;
        // the sum of all the exits performed on the validators
        // these values can be found in the execution layer block bodies under the withdrawals field
        // a withdrawal is considered exit if
        // - the epoch at which it happened is >= validator.withdrawableEpoch and in that case we only account for what would be <= 32 eth as exit
        // this value cannot decrease over reports
        uint256 validatorsExitedBalance;
        // the sum of all the exiting balance, which is all the validators on their way to get sweeped and exited
        // this includes voluntary exits and slashings
        // this value can decrease between reports
        uint256 validatorsExitingBalance;
        // the count of activated validators
        // even validators that are exited are still accounted
        // this value cannot decrease over reports
        uint32 validatorsCount;
        // an array containing the count of stopped validators per operator
        // the first element of the array is the sum of all stopped validators
        // then index 1 would be operator 0
        // these values cannot decrease over reports
        uint32[] stoppedValidatorCountPerOperator;
        // flag enabled by the oracles when the buffer rebalancing is activated
        // the activation logic is written in the oracle specification and all oracle members must agree on the activation
        // when active, the eth in the deposit buffer can be used to pay for exits in the redeem manager
        bool rebalanceDepositToRedeemMode;
        // flag enabled by the oracles when the slashing containment is activated
        // the activation logic is written in the oracle specification and all oracle members must agree on the activation
        // This flag is activated when a pre-defined threshold of slashed validators in our set of validators is reached
        // This flag is deactivated when a bottom threshold is met, this means that when we reach the upper threshold and activate the flag, we will deactivate it when we reach the bottom threshold and not before
        // when active, no more validator exits can be requested by the protocol
        bool slashingContainmentMode;
    }

    /// @notice The format of the oracle report in storage
    /// @notice These fields have the exact same function as the ones in ConsensusLayerReport, but this struct is optimized for storage
    struct StoredConsensusLayerReport {
        uint256 epoch;
        uint256 validatorsBalance;
        uint256 validatorsSkimmedBalance;
        uint256 validatorsExitedBalance;
        uint256 validatorsExitingBalance;
        uint32 validatorsCount;
        bool rebalanceDepositToRedeemMode;
        bool slashingContainmentMode;
    }

    /// @notice Get oracle address
    /// @return The oracle address
    function getOracle() external view returns (address);

    /// @notice Get CL validator total balance
    /// @return The CL Validator total balance
    function getCLValidatorTotalBalance() external view returns (uint256);

    /// @notice Get CL validator count (the amount of validator reported by the oracles)
    /// @return The CL validator count
    function getCLValidatorCount() external view returns (uint256);

    /// @notice Verifies if the provided epoch is valid
    /// @param epoch The epoch to lookup
    /// @return True if valid
    function isValidEpoch(uint256 epoch) external view returns (bool);

    /// @notice Retrieve the block timestamp
    /// @return The current timestamp from the EVM context
    function getTime() external view returns (uint256);

    /// @notice Retrieve expected epoch id
    /// @return The current expected epoch id
    function getExpectedEpochId() external view returns (uint256);

    /// @notice Retrieve the last completed epoch id
    /// @return The last completed epoch id
    function getLastCompletedEpochId() external view returns (uint256);

    /// @notice Retrieve the current epoch id based on block timestamp
    /// @return The current epoch id
    function getCurrentEpochId() external view returns (uint256);

    /// @notice Retrieve the current cl spec
    /// @return The Consensus Layer Specification
    function getCLSpec() external view returns (CLSpec.CLSpecStruct memory);

    /// @notice Retrieve the current frame details
    /// @return _startEpochId The epoch at the beginning of the frame
    /// @return _startTime The timestamp of the beginning of the frame in seconds
    /// @return _endTime The timestamp of the end of the frame in seconds
    function getCurrentFrame() external view returns (uint256 _startEpochId, uint256 _startTime, uint256 _endTime);

    /// @notice Retrieve the first epoch id of the frame of the provided epoch id
    /// @param _epochId Epoch id used to get the frame
    /// @return The first epoch id of the frame containing the given epoch id
    function getFrameFirstEpochId(uint256 _epochId) external view returns (uint256);

    /// @notice Retrieve the report bounds
    /// @return The report bounds
    function getReportBounds() external view returns (ReportBounds.ReportBoundsStruct memory);

    /// @notice Retrieve the last consensus layer report
    /// @return The stored consensus layer report
    function getLastConsensusLayerReport() external view returns (IOracleManagerV1.StoredConsensusLayerReport memory);

    /// @notice Set the oracle address
    /// @param _oracleAddress Address of the oracle
    function setOracle(address _oracleAddress) external;

    /// @notice Set the consensus layer spec
    /// @param _newValue The new consensus layer spec value
    function setCLSpec(CLSpec.CLSpecStruct calldata _newValue) external;

    /// @notice Set the report bounds
    /// @param _newValue The new report bounds value
    function setReportBounds(ReportBounds.ReportBoundsStruct calldata _newValue) external;

    /// @notice Performs all the reporting logics
    /// @param _report The consensus layer report structure
    function setConsensusLayerData(ConsensusLayerReport calldata _report) external;
}

File 5 of 25 : ISharesManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

/// @title Shares Manager Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the shares of the depositor and the ERC20 interface
interface ISharesManagerV1 is IERC20 {
    /// @notice Emitted when the total supply is changed
    event SetTotalSupply(uint256 totalSupply);

    /// @notice Balance too low to perform operation
    error BalanceTooLow();

    /// @notice Allowance too low to perform operation
    /// @param _from Account where funds are sent from
    /// @param _operator Account attempting the transfer
    /// @param _allowance Current allowance
    /// @param _value Requested transfer value in shares
    error AllowanceTooLow(address _from, address _operator, uint256 _allowance, uint256 _value);

    /// @notice Invalid empty transfer
    error NullTransfer();

    /// @notice Invalid transfer recipients
    /// @param _from Account sending the funds in the invalid transfer
    /// @param _to Account receiving the funds in the invalid transfer
    error UnauthorizedTransfer(address _from, address _to);

    /// @notice Retrieve the token name
    /// @return The token name
    function name() external pure returns (string memory);

    /// @notice Retrieve the token symbol
    /// @return The token symbol
    function symbol() external pure returns (string memory);

    /// @notice Retrieve the decimal count
    /// @return The decimal count
    function decimals() external pure returns (uint8);

    /// @notice Retrieve the total token supply
    /// @return The total supply in shares
    function totalSupply() external view returns (uint256);

    /// @notice Retrieve the total underlying asset supply
    /// @return The total underlying asset supply
    function totalUnderlyingSupply() external view returns (uint256);

    /// @notice Retrieve the balance of an account
    /// @param _owner Address to be checked
    /// @return The balance of the account in shares
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Retrieve the underlying asset balance of an account
    /// @param _owner Address to be checked
    /// @return The underlying balance of the account
    function balanceOfUnderlying(address _owner) external view returns (uint256);

    /// @notice Retrieve the underlying asset balance from an amount of shares
    /// @param _shares Amount of shares to convert
    /// @return The underlying asset balance represented by the shares
    function underlyingBalanceFromShares(uint256 _shares) external view returns (uint256);

    /// @notice Retrieve the shares count from an underlying asset amount
    /// @param _underlyingAssetAmount Amount of underlying asset to convert
    /// @return The amount of shares worth the underlying asset amopunt
    function sharesFromUnderlyingBalance(uint256 _underlyingAssetAmount) external view returns (uint256);

    /// @notice Retrieve the allowance value for a spender
    /// @param _owner Address that issued the allowance
    /// @param _spender Address that received the allowance
    /// @return The allowance in shares for a given spender
    function allowance(address _owner, address _spender) external view returns (uint256);

    /// @notice Performs a transfer from the message sender to the provided account
    /// @param _to Address receiving the tokens
    /// @param _value Amount of shares to be sent
    /// @return True if success
    function transfer(address _to, uint256 _value) external returns (bool);

    /// @notice Performs a transfer between two recipients
    /// @param _from Address sending the tokens
    /// @param _to Address receiving the tokens
    /// @param _value Amount of shares to be sent
    /// @return True if success
    function transferFrom(address _from, address _to, uint256 _value) external returns (bool);

    /// @notice Approves an account for future spendings
    /// @dev An approved account can use transferFrom to transfer funds on behalf of the token owner
    /// @param _spender Address that is allowed to spend the tokens
    /// @param _value The allowed amount in shares, will override previous value
    /// @return True if success
    function approve(address _spender, uint256 _value) external returns (bool);

    /// @notice Increase allowance to another account
    /// @param _spender Spender that receives the allowance
    /// @param _additionalValue Amount of shares to add
    /// @return True if success
    function increaseAllowance(address _spender, uint256 _additionalValue) external returns (bool);

    /// @notice Decrease allowance to another account
    /// @param _spender Spender that receives the allowance
    /// @param _subtractableValue Amount of shares to subtract
    /// @return True if success
    function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool);
}

File 6 of 25 : IUserDepositManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title User Deposit Manager (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the inbound transfers cases or the explicit submissions
interface IUserDepositManagerV1 {
    /// @notice User deposited ETH in the system
    /// @param depositor Address performing the deposit
    /// @param recipient Address receiving the minted shares
    /// @param amount Amount in ETH deposited
    event UserDeposit(address indexed depositor, address indexed recipient, uint256 amount);

    /// @notice And empty deposit attempt was made
    error EmptyDeposit();

    /// @notice Explicit deposit method to mint on msg.sender
    function deposit() external payable;

    /// @notice Explicit deposit method to mint on msg.sender and transfer to _recipient
    /// @param _recipient Address receiving the minted LsETH
    function depositAndTransfer(address _recipient) external payable;

    /// @notice Implicit deposit method, when the user performs a regular transfer to the contract
    receive() external payable;

    /// @notice Invalid call, when the user sends a transaction with a data payload but no method matched
    fallback() external payable;
}

File 7 of 25 : IAllowlist.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Allowlist Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the list of allowed recipients.
interface IAllowlistV1 {
    /// @notice The permissions of several accounts have changed
    /// @param accounts List of accounts
    /// @param permissions New permissions for each account at the same index
    event SetAllowlistPermissions(address[] accounts, uint256[] permissions);

    /// @notice The stored allower address has been changed
    /// @param allower The new allower address
    event SetAllower(address indexed allower);

    /// @notice The provided accounts list is empty
    error InvalidAlloweeCount();

    /// @notice The account is denied access
    /// @param _account The denied account
    error Denied(address _account);

    /// @notice The provided accounts and permissions list have different lengths
    error MismatchedAlloweeAndStatusCount();

    /// @notice Initializes the allowlist
    /// @param _admin Address of the Allowlist administrator
    /// @param _allower Address of the allower
    function initAllowlistV1(address _admin, address _allower) external;

    /// @notice Retrieves the allower address
    /// @return The address of the allower
    function getAllower() external view returns (address);

    /// @notice This method returns true if the user has the expected permission and
    ///         is not in the deny list
    /// @param _account Recipient to verify
    /// @param _mask Combination of permissions to verify
    /// @return True if mask is respected and user is allowed
    function isAllowed(address _account, uint256 _mask) external view returns (bool);

    /// @notice This method returns true if the user is in the deny list
    /// @param _account Recipient to verify
    /// @return True if user is denied access
    function isDenied(address _account) external view returns (bool);

    /// @notice This method returns true if the user has the expected permission
    ///         ignoring any deny list membership
    /// @param _account Recipient to verify
    /// @param _mask Combination of permissions to verify
    /// @return True if mask is respected
    function hasPermission(address _account, uint256 _mask) external view returns (bool);

    /// @notice This method retrieves the raw permission value
    /// @param _account Recipient to verify
    /// @return The raw permissions value of the account
    function getPermissions(address _account) external view returns (uint256);

    /// @notice This method should be used as a modifier and is expected to revert
    ///         if the user hasn't got the required permission or if the user is
    ///         in the deny list.
    /// @param _account Recipient to verify
    /// @param _mask Combination of permissions to verify
    function onlyAllowed(address _account, uint256 _mask) external view;

    /// @notice Changes the allower address
    /// @param _newAllowerAddress New address allowed to edit the allowlist
    function setAllower(address _newAllowerAddress) external;

    /// @notice Sets the allowlisting status for one or more accounts
    /// @dev The permission value is overridden and not updated
    /// @param _accounts Accounts with statuses to edit
    /// @param _permissions Allowlist permissions for each account, in the same order as _accounts
    function allow(address[] calldata _accounts, uint256[] calldata _permissions) external;
}

File 8 of 25 : IRedeemManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../state/redeemManager/RedeemQueue.sol";
import "../state/redeemManager/WithdrawalStack.sol";

/// @title Redeem Manager Interface (v1)
/// @author Kiln
/// @notice This contract handles the redeem requests of all users
interface IRedeemManagerV1 {
    /// @notice Emitted when a redeem request is created
    /// @param owner The owner of the redeem request
    /// @param height The height of the redeem request in LsETH
    /// @param amount The amount of the redeem request in LsETH
    /// @param maxRedeemableEth The maximum amount of eth that can be redeemed from this request
    /// @param id The id of the new redeem request
    event RequestedRedeem(address indexed owner, uint256 height, uint256 amount, uint256 maxRedeemableEth, uint32 id);

    /// @notice Emitted when a withdrawal event is created
    /// @param height The height of the withdrawal event in LsETH
    /// @param amount The amount of the withdrawal event in LsETH
    /// @param ethAmount The amount of eth to distrubute to claimers
    /// @param id The id of the withdrawal event
    event ReportedWithdrawal(uint256 height, uint256 amount, uint256 ethAmount, uint32 id);

    /// @notice Emitted when a redeem request has been satisfied and filled (even partially) from a withdrawal event
    /// @param redeemRequestId The id of the redeem request
    /// @param withdrawalEventId The id of the withdrawal event used to fill the request
    /// @param lsEthAmountSatisfied The amount of LsETH filled
    /// @param ethAmountSatisfied The amount of ETH filled
    /// @param lsEthAmountRemaining The amount of LsETH remaining
    /// @param ethAmountExceeding The amount of eth added to the exceeding buffer
    event SatisfiedRedeemRequest(
        uint32 indexed redeemRequestId,
        uint32 indexed withdrawalEventId,
        uint256 lsEthAmountSatisfied,
        uint256 ethAmountSatisfied,
        uint256 lsEthAmountRemaining,
        uint256 ethAmountExceeding
    );

    /// @notice Emitted when a redeem request claim has been processed and matched at least once and funds are sent to the recipient
    /// @param redeemRequestId The id of the redeem request
    /// @param recipient The address receiving the redeem request funds
    /// @param ethAmount The amount of eth retrieved
    /// @param lsEthAmount The total amount of LsETH used to redeem the eth
    /// @param remainingLsEthAmount The amount of LsETH remaining
    event ClaimedRedeemRequest(
        uint32 indexed redeemRequestId,
        address indexed recipient,
        uint256 ethAmount,
        uint256 lsEthAmount,
        uint256 remainingLsEthAmount
    );

    /// @notice Emitted when the redeem demand is set
    /// @param oldRedeemDemand The old redeem demand
    /// @param newRedeemDemand The new redeem demand
    event SetRedeemDemand(uint256 oldRedeemDemand, uint256 newRedeemDemand);

    /// @notice Emitted when the River address is set
    /// @param river The new river address
    event SetRiver(address river);

    /// @notice Thrown When a zero value is provided
    error InvalidZeroAmount();

    /// @notice Thrown when a transfer error occured with LsETH
    error TransferError();

    /// @notice Thrown when the provided arrays don't have matching lengths
    error IncompatibleArrayLengths();

    /// @notice Thrown when the provided redeem request id is out of bounds
    /// @param id The redeem request id
    error RedeemRequestOutOfBounds(uint256 id);

    /// @notice Thrown when the withdrawal request id if out of bounds
    /// @param id The withdrawal event id
    error WithdrawalEventOutOfBounds(uint256 id);

    /// @notice Thrown when	the redeem request id is already claimed
    /// @param id The redeem request id
    error RedeemRequestAlreadyClaimed(uint256 id);

    /// @notice Thrown when the redeem request and withdrawal event are not matching during claim
    /// @param redeemRequestId The provided redeem request id
    /// @param withdrawalEventId The provided associated withdrawal event id
    error DoesNotMatch(uint256 redeemRequestId, uint256 withdrawalEventId);

    /// @notice Thrown when the provided withdrawal event exceeds the redeem demand
    /// @param withdrawalAmount The amount of the withdrawal event
    /// @param redeemDemand The current redeem demand
    error WithdrawalExceedsRedeemDemand(uint256 withdrawalAmount, uint256 redeemDemand);

    /// @notice Thrown when the payment after a claim failed
    /// @param recipient The recipient of the payment
    /// @param rdata The revert data
    error ClaimRedeemFailed(address recipient, bytes rdata);

    /// @param _river The address of the River contract
    function initializeRedeemManagerV1(address _river) external;

    /// @notice Retrieve River address
    /// @return The address of River
    function getRiver() external view returns (address);

    /// @notice Retrieve the global count of redeem requests
    function getRedeemRequestCount() external view returns (uint256);

    /// @notice Retrieve the details of a specific redeem request
    /// @param _redeemRequestId The id of the request
    /// @return The redeem request details
    function getRedeemRequestDetails(uint32 _redeemRequestId)
        external
        view
        returns (RedeemQueue.RedeemRequest memory);

    /// @notice Retrieve the global count of withdrawal events
    function getWithdrawalEventCount() external view returns (uint256);

    /// @notice Retrieve the details of a specific withdrawal event
    /// @param _withdrawalEventId The id of the withdrawal event
    /// @return The withdrawal event details
    function getWithdrawalEventDetails(uint32 _withdrawalEventId)
        external
        view
        returns (WithdrawalStack.WithdrawalEvent memory);

    /// @notice Retrieve the amount of redeemed LsETH pending to be supplied with withdrawn ETH
    /// @return The amount of eth in the buffer
    function getBufferedExceedingEth() external view returns (uint256);

    /// @notice Retrieve the amount of LsETH waiting to be exited
    /// @return The amount of LsETH waiting to be exited
    function getRedeemDemand() external view returns (uint256);

    /// @notice Resolves the provided list of redeem request ids
    /// @dev The result is an array of equal length with ids or error code
    /// @dev -1 means that the request is not satisfied yet
    /// @dev -2 means that the request is out of bounds
    /// @dev -3 means that the request has already been claimed
    /// @dev This call was created to be called by an off-chain interface, the output could then be used to perform the claimRewards call in a regular transaction
    /// @param _redeemRequestIds The list of redeem requests to resolve
    /// @return withdrawalEventIds The list of withdrawal events matching every redeem request (or error codes)
    function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
        external
        view
        returns (int64[] memory withdrawalEventIds);

    /// @notice Creates a redeem request
    /// @param _lsETHAmount The amount of LsETH to redeem
    /// @param _recipient The recipient owning the redeem request
    /// @return redeemRequestId The id of the redeem request
    function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 redeemRequestId);

    /// @notice Creates a redeem request using msg.sender as recipient
    /// @param _lsETHAmount The amount of LsETH to redeem
    /// @return redeemRequestId The id of the redeem request
    function requestRedeem(uint256 _lsETHAmount) external returns (uint32 redeemRequestId);

    /// @notice Claims the rewards of the provided redeem request ids
    /// @param _redeemRequestIds The list of redeem requests to claim
    /// @param _withdrawalEventIds The list of withdrawal events to use for every redeem request claim
    /// @param _skipAlreadyClaimed True if the call should not revert on claiming of already claimed requests
    /// @param _depth The maximum recursive depth for the resolution of the redeem requests
    /// @return claimStatuses The list of claim statuses. 0 for fully claimed, 1 for partially claimed, 2 for skipped
    function claimRedeemRequests(
        uint32[] calldata _redeemRequestIds,
        uint32[] calldata _withdrawalEventIds,
        bool _skipAlreadyClaimed,
        uint16 _depth
    ) external returns (uint8[] memory claimStatuses);

    /// @notice Claims the rewards of the provided redeem request ids
    /// @param _redeemRequestIds The list of redeem requests to claim
    /// @param _withdrawalEventIds The list of withdrawal events to use for every redeem request claim
    /// @return claimStatuses The list of claim statuses. 0 for fully claimed, 1 for partially claimed, 2 for skipped
    function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
        external
        returns (uint8[] memory claimStatuses);

    /// @notice Reports a withdraw event from River
    /// @param _lsETHWithdrawable The amount of LsETH that can be redeemed due to this new withdraw event
    function reportWithdraw(uint256 _lsETHWithdrawable) external payable;

    /// @notice Pulls exceeding buffer eth
    /// @param _max The maximum amount that should be pulled
    function pullExceedingEth(uint256 _max) external;
}

File 9 of 25 : IRiver.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../state/river/DailyCommittableLimits.sol";

import "./components/IConsensusLayerDepositManager.1.sol";
import "./components/IOracleManager.1.sol";
import "./components/ISharesManager.1.sol";
import "./components/IUserDepositManager.1.sol";

/// @title River Interface (v1)
/// @author Kiln
/// @notice The main system interface
interface IRiverV1 is IConsensusLayerDepositManagerV1, IUserDepositManagerV1, ISharesManagerV1, IOracleManagerV1 {
    /// @notice Funds have been pulled from the Execution Layer Fee Recipient
    /// @param amount The amount pulled
    event PulledELFees(uint256 amount);

    /// @notice Funds have been pulled from the Coverage Fund
    /// @param amount The amount pulled
    event PulledCoverageFunds(uint256 amount);

    /// @notice Emitted when funds are pulled from the redeem manager
    /// @param amount The amount pulled
    event PulledRedeemManagerExceedingEth(uint256 amount);

    /// @notice Emitted when funds are pulled from the CL recipient
    /// @param pulledSkimmedEthAmount The amount of skimmed ETH pulled
    /// @param pullExitedEthAmount The amount of exited ETH pulled
    event PulledCLFunds(uint256 pulledSkimmedEthAmount, uint256 pullExitedEthAmount);

    /// @notice The stored Execution Layer Fee Recipient has been changed
    /// @param elFeeRecipient The new Execution Layer Fee Recipient
    event SetELFeeRecipient(address indexed elFeeRecipient);

    /// @notice The stored Coverage Fund has been changed
    /// @param coverageFund The new Coverage Fund
    event SetCoverageFund(address indexed coverageFund);

    /// @notice The stored Collector has been changed
    /// @param collector The new Collector
    event SetCollector(address indexed collector);

    /// @notice The stored Allowlist has been changed
    /// @param allowlist The new Allowlist
    event SetAllowlist(address indexed allowlist);

    /// @notice The stored Global Fee has been changed
    /// @param fee The new Global Fee
    event SetGlobalFee(uint256 fee);

    /// @notice The stored Operators Registry has been changed
    /// @param operatorRegistry The new Operators Registry
    event SetOperatorsRegistry(address indexed operatorRegistry);

    /// @notice The stored Metadata URI string has been changed
    /// @param metadataURI The new Metadata URI string
    event SetMetadataURI(string metadataURI);

    /// @notice The system underlying supply increased. This is a snapshot of the balances for accounting purposes
    /// @param _collector The address of the collector during this event
    /// @param _oldTotalUnderlyingBalance Old total ETH balance under management by River
    /// @param _oldTotalSupply Old total supply in shares
    /// @param _newTotalUnderlyingBalance New total ETH balance under management by River
    /// @param _newTotalSupply New total supply in shares
    event RewardsEarned(
        address indexed _collector,
        uint256 _oldTotalUnderlyingBalance,
        uint256 _oldTotalSupply,
        uint256 _newTotalUnderlyingBalance,
        uint256 _newTotalSupply
    );

    /// @notice Emitted when the daily committable limits are changed
    /// @param minNetAmount The minimum amount that must be used as the daily committable amount
    /// @param maxRelativeAmount The maximum amount that can be used as the daily committable amount, relative to the total underlying supply
    event SetMaxDailyCommittableAmounts(uint256 minNetAmount, uint256 maxRelativeAmount);

    /// @notice Emitted when the redeem manager address is changed
    /// @param redeemManager The address of the redeem manager
    event SetRedeemManager(address redeemManager);

    /// @notice Emitted when the balance to deposit is updated
    /// @param oldAmount The old balance to deposit
    /// @param newAmount The new balance to deposit
    event SetBalanceToDeposit(uint256 oldAmount, uint256 newAmount);

    /// @notice Emitted when the balance to redeem is updated
    /// @param oldAmount The old balance to redeem
    /// @param newAmount The new balance to redeem
    event SetBalanceToRedeem(uint256 oldAmount, uint256 newAmount);

    /// @notice Emitted when the balance committed to deposit
    /// @param oldAmount The old balance committed to deposit
    /// @param newAmount The new balance committed to deposit
    event SetBalanceCommittedToDeposit(uint256 oldAmount, uint256 newAmount);

    /// @notice Emitted when the redeem manager received a withdraw event report
    /// @param redeemManagerDemand The total demand in LsETH of the redeem manager
    /// @param suppliedRedeemManagerDemand The amount of LsETH demand actually supplied
    /// @param suppliedRedeemManagerDemandInEth The amount in ETH of the supplied demand
    event ReportedRedeemManager(
        uint256 redeemManagerDemand, uint256 suppliedRedeemManagerDemand, uint256 suppliedRedeemManagerDemandInEth
    );

    /// @notice Thrown when the amount received from the Withdraw contract doe not match the requested amount
    /// @param requested The amount that was requested
    /// @param received The amount that was received
    error InvalidPulledClFundsAmount(uint256 requested, uint256 received);

    /// @notice The computed amount of shares to mint is 0
    error ZeroMintedShares();

    /// @notice The access was denied
    /// @param account The account that was denied
    error Denied(address account);

    /// @notice Initializes the River system
    /// @param _depositContractAddress Address to make Consensus Layer deposits
    /// @param _elFeeRecipientAddress Address that receives the execution layer fees
    /// @param _withdrawalCredentials Credentials to use for every validator deposit
    /// @param _oracleAddress The address of the Oracle contract
    /// @param _systemAdministratorAddress Administrator address
    /// @param _allowlistAddress Address of the allowlist contract
    /// @param _operatorRegistryAddress Address of the operator registry
    /// @param _collectorAddress Address receiving the the global fee on revenue
    /// @param _globalFee Amount retained when the ETH balance increases and sent to the collector
    function initRiverV1(
        address _depositContractAddress,
        address _elFeeRecipientAddress,
        bytes32 _withdrawalCredentials,
        address _oracleAddress,
        address _systemAdministratorAddress,
        address _allowlistAddress,
        address _operatorRegistryAddress,
        address _collectorAddress,
        uint256 _globalFee
    ) external;

    /// @notice Initialized version 1.1 of the River System
    /// @param _redeemManager The redeem manager address
    /// @param _epochsPerFrame The amounts of epochs in a frame
    /// @param _slotsPerEpoch The slots inside an epoch
    /// @param _secondsPerSlot The seconds inside a slot
    /// @param _genesisTime The genesis timestamp
    /// @param _epochsToAssumedFinality The number of epochs before an epoch is considered final on-chain
    /// @param _annualAprUpperBound The reporting upper bound
    /// @param _relativeLowerBound The reporting lower bound
    /// @param _maxDailyNetCommittableAmount_ The net daily committable limit
    /// @param _maxDailyRelativeCommittableAmount_ The relative daily committable limit
    function initRiverV1_1(
        address _redeemManager,
        uint64 _epochsPerFrame,
        uint64 _slotsPerEpoch,
        uint64 _secondsPerSlot,
        uint64 _genesisTime,
        uint64 _epochsToAssumedFinality,
        uint256 _annualAprUpperBound,
        uint256 _relativeLowerBound,
        uint128 _maxDailyNetCommittableAmount_,
        uint128 _maxDailyRelativeCommittableAmount_
    ) external;

    /// @notice Initializes version 1.2 of the River System
    function initRiverV1_2() external;

    /// @notice Get the current global fee
    /// @return The global fee
    function getGlobalFee() external view returns (uint256);

    /// @notice Retrieve the allowlist address
    /// @return The allowlist address
    function getAllowlist() external view returns (address);

    /// @notice Retrieve the collector address
    /// @return The collector address
    function getCollector() external view returns (address);

    /// @notice Retrieve the execution layer fee recipient
    /// @return The execution layer fee recipient address
    function getELFeeRecipient() external view returns (address);

    /// @notice Retrieve the coverage fund
    /// @return The coverage fund address
    function getCoverageFund() external view returns (address);

    /// @notice Retrieve the redeem manager
    /// @return The redeem manager address
    function getRedeemManager() external view returns (address);

    /// @notice Retrieve the operators registry
    /// @return The operators registry address
    function getOperatorsRegistry() external view returns (address);

    /// @notice Retrieve the metadata uri string value
    /// @return The metadata uri string value
    function getMetadataURI() external view returns (string memory);

    /// @notice Retrieve the configured daily committable limits
    /// @return The daily committable limits structure
    function getDailyCommittableLimits()
        external
        view
        returns (DailyCommittableLimits.DailyCommittableLimitsStruct memory);

    /// @notice Resolves the provided redeem requests by calling the redeem manager
    /// @param _redeemRequestIds The list of redeem requests to resolve
    /// @return withdrawalEventIds The list of matching withdrawal events, or error codes
    function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
        external
        view
        returns (int64[] memory withdrawalEventIds);

    /// @notice Set the daily committable limits
    /// @param _dcl The Daily Committable Limits structure
    function setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl) external;

    /// @notice Retrieve the current balance to redeem
    /// @return The current balance to redeem
    function getBalanceToRedeem() external view returns (uint256);

    /// @notice Performs a redeem request on the redeem manager
    /// @param _lsETHAmount The amount of LsETH to redeem
    /// @param _recipient The address that will own the redeem request
    /// @return redeemRequestId The ID of the newly created redeem request
    function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 redeemRequestId);

    /// @notice Claims several redeem requests
    /// @param _redeemRequestIds The list of redeem requests to claim
    /// @param _withdrawalEventIds The list of resolved withdrawal event ids
    /// @return claimStatuses The operation status results
    function claimRedeemRequests(uint32[] calldata _redeemRequestIds, uint32[] calldata _withdrawalEventIds)
        external
        returns (uint8[] memory claimStatuses);

    /// @notice Changes the global fee parameter
    /// @param _newFee New fee value
    function setGlobalFee(uint256 _newFee) external;

    /// @notice Changes the allowlist address
    /// @param _newAllowlist New address for the allowlist
    function setAllowlist(address _newAllowlist) external;

    /// @notice Changes the collector address
    /// @param _newCollector New address for the collector
    function setCollector(address _newCollector) external;

    /// @notice Changes the execution layer fee recipient
    /// @param _newELFeeRecipient New address for the recipient
    function setELFeeRecipient(address _newELFeeRecipient) external;

    /// @notice Changes the coverage fund
    /// @param _newCoverageFund New address for the fund
    function setCoverageFund(address _newCoverageFund) external;

    /// @notice Sets the metadata uri string value
    /// @param _metadataURI The new metadata uri string value
    function setMetadataURI(string memory _metadataURI) external;

    /// @notice Input for execution layer fee earnings
    function sendELFees() external payable;

    /// @notice Input for consensus layer funds, containing both exit and skimming
    function sendCLFunds() external payable;

    /// @notice Input for coverage funds
    function sendCoverageFunds() external payable;

    /// @notice Input for the redeem manager funds
    function sendRedeemManagerExceedingFunds() external payable;
}

File 10 of 25 : LibAllowlistMasks.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Lib Allowlist Masks
/// @notice Holds all the mask values
library LibAllowlistMasks {
    /// @notice Mask used for denied accounts
    uint256 internal constant DENY_MASK = 0x1 << 255;
    /// @notice The mask for the deposit right
    uint256 internal constant DEPOSIT_MASK = 0x1;
    /// @notice The mask for the donation right
    uint256 internal constant DONATE_MASK = 0x1 << 1;
    /// @notice The mask for the redeem right
    uint256 internal constant REDEEM_MASK = 0x1 << 2;
}

File 11 of 25 : LibBasisPoints.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Lib Basis Points
/// @notice Holds the basis points max value
library LibBasisPoints {
    /// @notice The max value for basis points (represents 100%)
    uint256 internal constant BASIS_POINTS_MAX = 10_000;
}

File 12 of 25 : LibErrors.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

/// @title Lib Errors
/// @notice Library of common errors
library LibErrors {
    /// @notice The operator is unauthorized for the caller
    /// @param caller Address performing the call
    error Unauthorized(address caller);

    /// @notice The call was invalid
    error InvalidCall();

    /// @notice The argument was invalid
    error InvalidArgument();

    /// @notice The address is zero
    error InvalidZeroAddress();

    /// @notice The string is empty
    error InvalidEmptyString();

    /// @notice The fee is invalid
    error InvalidFee();
}

File 13 of 25 : LibSanitize.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "./LibErrors.sol";
import "./LibBasisPoints.sol";

/// @title Lib Sanitize
/// @notice Utilities to sanitize input values
library LibSanitize {
    /// @notice Reverts if address is 0
    /// @param _address Address to check
    function _notZeroAddress(address _address) internal pure {
        if (_address == address(0)) {
            revert LibErrors.InvalidZeroAddress();
        }
    }

    /// @notice Reverts if string is empty
    /// @param _string String to check
    function _notEmptyString(string memory _string) internal pure {
        if (bytes(_string).length == 0) {
            revert LibErrors.InvalidEmptyString();
        }
    }

    /// @notice Reverts if fee is invalid
    /// @param _fee Fee to check
    function _validFee(uint256 _fee) internal pure {
        if (_fee > LibBasisPoints.BASIS_POINTS_MAX) {
            revert LibErrors.InvalidFee();
        }
    }
}

File 14 of 25 : LibUint256.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

/// @title Lib Uint256
/// @notice Utilities to perform uint operations
library LibUint256 {
    /// @notice Converts a value to little endian (64 bits)
    /// @param _value The value to convert
    /// @return result The converted value
    function toLittleEndian64(uint256 _value) internal pure returns (uint256 result) {
        result = 0;
        uint256 tempValue = _value;
        result = tempValue & 0xFF;
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        result = (result << 8) | (tempValue & 0xFF);
        tempValue >>= 8;

        assert(0 == tempValue); // fully converted
        result <<= (24 * 8);
    }

    /// @notice Returns the minimum value
    /// @param _a First value
    /// @param _b Second value
    /// @return Smallest value between _a and _b
    function min(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a > _b ? _b : _a);
    }

    /// @notice Returns the max value
    /// @param _a First value
    /// @param _b Second value
    /// @return Highest value between _a and _b
    function max(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a < _b ? _b : _a);
    }

    /// @notice Performs a ceiled division
    /// @param _a Numerator
    /// @param _b Denominator
    /// @return ceil(_a / _b)
    function ceil(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a / _b) + (_a % _b > 0 ? 1 : 0);
    }
}

File 15 of 25 : LibUnstructuredStorage.sol
// SPDX-License-Identifier:    MIT

pragma solidity 0.8.10;

/// @title Lib Unstructured Storage
/// @notice Utilities to work with unstructured storage
library LibUnstructuredStorage {
    /// @notice Retrieve a bool value at a storage slot
    /// @param _position The storage slot to retrieve
    /// @return data The bool value
    function getStorageBool(bytes32 _position) internal view returns (bool data) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            data := sload(_position)
        }
    }

    /// @notice Retrieve an address value at a storage slot
    /// @param _position The storage slot to retrieve
    /// @return data The address value
    function getStorageAddress(bytes32 _position) internal view returns (address data) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            data := sload(_position)
        }
    }

    /// @notice Retrieve a bytes32 value at a storage slot
    /// @param _position The storage slot to retrieve
    /// @return data The bytes32 value
    function getStorageBytes32(bytes32 _position) internal view returns (bytes32 data) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            data := sload(_position)
        }
    }

    /// @notice Retrieve an uint256 value at a storage slot
    /// @param _position The storage slot to retrieve
    /// @return data The uint256 value
    function getStorageUint256(bytes32 _position) internal view returns (uint256 data) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            data := sload(_position)
        }
    }

    /// @notice Sets a bool value at a storage slot
    /// @param _position The storage slot to set
    /// @param _data The bool value to set
    function setStorageBool(bytes32 _position, bool _data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(_position, _data)
        }
    }

    /// @notice Sets an address value at a storage slot
    /// @param _position The storage slot to set
    /// @param _data The address value to set
    function setStorageAddress(bytes32 _position, address _data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(_position, _data)
        }
    }

    /// @notice Sets a bytes32 value at a storage slot
    /// @param _position The storage slot to set
    /// @param _data The bytes32 value to set
    function setStorageBytes32(bytes32 _position, bytes32 _data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(_position, _data)
        }
    }

    /// @notice Sets an uint256 value at a storage slot
    /// @param _position The storage slot to set
    /// @param _data The uint256 value to set
    function setStorageUint256(bytes32 _position, uint256 _data) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(_position, _data)
        }
    }
}

File 16 of 25 : BufferedExceedingEth.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibUnstructuredStorage.sol";

/// @title Buffered Exceeding Eth storage
/// @notice Redeen Manager utility to manage the exceeding ETH with a redeem request
library BufferedExceedingEth {
    /// @notice Storage slot of the Redeem Buffered Eth
    bytes32 internal constant BUFFERED_EXCEEDING_ETH_SLOT =
        bytes32(uint256(keccak256("river.state.bufferedExceedingEth")) - 1);

    /// @notice Retrieve the Redeem Buffered Eth Value
    /// @return The Redeem Buffered Eth Value
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(BUFFERED_EXCEEDING_ETH_SLOT);
    }

    /// @notice Sets the Redeem Buffered Eth Value
    /// @param newValue The new value
    function set(uint256 newValue) internal {
        LibUnstructuredStorage.setStorageUint256(BUFFERED_EXCEEDING_ETH_SLOT, newValue);
    }
}

File 17 of 25 : RedeemDemand.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibUnstructuredStorage.sol";

/// @title Redeem Demand storage
/// @notice Redeem Manager utility to store the current demand in LsETH
library RedeemDemand {
    /// @notice Storage slot of the Redeem Demand
    bytes32 internal constant REDEEM_DEMAND_SLOT = bytes32(uint256(keccak256("river.state.redeemDemand")) - 1);

    /// @notice Retrieve the Redeem Demand Value
    /// @return The Redeem Demand Value
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(REDEEM_DEMAND_SLOT);
    }

    /// @notice Sets the Redeem Demand Value
    /// @param newValue The new value
    function set(uint256 newValue) internal {
        LibUnstructuredStorage.setStorageUint256(REDEEM_DEMAND_SLOT, newValue);
    }
}

File 18 of 25 : RedeemQueue.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Redeem Manager Redeem Queue storage
/// @notice Utility to manage the Redeem Queue in the Redeem Manager
library RedeemQueue {
    /// @notice Storage slot of the Redeem Queue
    bytes32 internal constant REDEEM_QUEUE_ID_SLOT = bytes32(uint256(keccak256("river.state.redeemQueue")) - 1);

    /// @notice The Redeemer structure represents the redeem request made by a user
    struct RedeemRequest {
        /// @custom:attribute The amount of the redeem request in LsETH
        uint256 amount;
        /// @custom:attribute The maximum amount of ETH redeemable by this request
        uint256 maxRedeemableEth;
        /// @custom:attribute The owner of the redeem request
        address owner;
        /// @custom:attribute The height is the cumulative sum of all the sizes of preceding redeem requests
        uint256 height;
    }

    /// @notice Retrieve the Redeem Queue array storage pointer
    /// @return data The Redeem Queue array storage pointer
    function get() internal pure returns (RedeemRequest[] storage data) {
        bytes32 position = REDEEM_QUEUE_ID_SLOT;
        assembly {
            data.slot := position
        }
    }
}

File 19 of 25 : WithdrawalStack.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Redeem Manager Withdrawal Stack storage
/// @notice Utility to manage the Withdrawal Stack in the Redeem Manager
library WithdrawalStack {
    /// @notice Storage slot of the Withdrawal Stack
    bytes32 internal constant WITHDRAWAL_STACK_ID_SLOT = bytes32(uint256(keccak256("river.state.withdrawalStack")) - 1);

    /// @notice The Redeemer structure represents the withdrawal events made by River
    struct WithdrawalEvent {
        /// @custom:attribute The amount of the withdrawal event in LsETH
        uint256 amount;
        /// @custom:attribute The amount of the withdrawal event in ETH
        uint256 withdrawnEth;
        /// @custom:attribute The height is the cumulative sum of all the sizes of preceding withdrawal events
        uint256 height;
    }

    /// @notice Retrieve the Withdrawal Stack array storage pointer
    /// @return data The Withdrawal Stack array storage pointer
    function get() internal pure returns (WithdrawalEvent[] storage data) {
        bytes32 position = WITHDRAWAL_STACK_ID_SLOT;
        assembly {
            data.slot := position
        }
    }
}

File 20 of 25 : CLSpec.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Consensus Layer Spec Storage
/// @notice Utility to manage the Consensus Layer Spec in storage
library CLSpec {
    /// @notice Storage slot of the Consensus Layer Spec
    bytes32 internal constant CL_SPEC_SLOT = bytes32(uint256(keccak256("river.state.clSpec")) - 1);

    /// @notice The Consensus Layer Spec structure
    struct CLSpecStruct {
        /// @custom:attribute The count of epochs per frame, 225 means 24h
        uint64 epochsPerFrame;
        /// @custom:attribute The count of slots in an epoch (32 on mainnet)
        uint64 slotsPerEpoch;
        /// @custom:attribute The seconds in a slot (12 on mainnet)
        uint64 secondsPerSlot;
        /// @custom:attribute The block timestamp of the first consensus layer block
        uint64 genesisTime;
        /// @custom:attribute The count of epochs before considering an epoch final on-chain
        uint64 epochsToAssumedFinality;
    }

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The structure in storage
        CLSpecStruct value;
    }

    /// @notice Retrieve the Consensus Layer Spec from storage
    /// @return The Consensus Layer Spec
    function get() internal view returns (CLSpecStruct memory) {
        bytes32 slot = CL_SPEC_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        return r.value;
    }

    /// @notice Set the Consensus Layer Spec value in storage
    /// @param _newCLSpec The new value to set in storage
    function set(CLSpecStruct memory _newCLSpec) internal {
        bytes32 slot = CL_SPEC_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        r.value = _newCLSpec;
    }
}

File 21 of 25 : DailyCommittableLimits.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibSanitize.sol";

/// @title Daily Committable Limits storage
/// @notice Utility to manage the Daily Committable Limits in storage
library DailyCommittableLimits {
    /// @notice Storage slot of the Daily Committable Limits storage
    bytes32 internal constant DAILY_COMMITTABLE_LIMITS_SLOT =
        bytes32(uint256(keccak256("river.state.dailyCommittableLimits")) - 1);

    /// @notice The daily committable limits structure
    struct DailyCommittableLimitsStruct {
        uint128 minDailyNetCommittableAmount;
        uint128 maxDailyRelativeCommittableAmount;
    }

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The structure in storage
        DailyCommittableLimitsStruct value;
    }

    /// @notice Retrieve the Daily Committable Limits from storage
    /// @return The Daily Committable Limits
    function get() internal view returns (DailyCommittableLimitsStruct memory) {
        bytes32 slot = DAILY_COMMITTABLE_LIMITS_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        return r.value;
    }

    /// @notice Set the Daily Committable Limits value in storage
    /// @param _newValue The new value to set in storage
    function set(DailyCommittableLimitsStruct memory _newValue) internal {
        bytes32 slot = DAILY_COMMITTABLE_LIMITS_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        r.value = _newValue;
    }
}

File 22 of 25 : ReportBounds.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Report Bounds Storage
/// @notice Utility to manage the Report Bounds in storage
library ReportBounds {
    /// @notice Storage slot of the Report Bounds
    bytes32 internal constant REPORT_BOUNDS_SLOT = bytes32(uint256(keccak256("river.state.reportBounds")) - 1);

    /// @notice The Report Bounds structure
    struct ReportBoundsStruct {
        /// @custom:attribute The maximum allowed annual apr, checked before submitting a report to River
        uint256 annualAprUpperBound;
        /// @custom:attribute The maximum allowed balance decrease, also checked before submitting a report to River
        uint256 relativeLowerBound;
    }

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The structure in storage
        ReportBoundsStruct value;
    }

    /// @notice Retrieve the Report Bounds from storage
    /// @return The Report Bounds
    function get() internal view returns (ReportBoundsStruct memory) {
        bytes32 slot = REPORT_BOUNDS_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        return r.value;
    }

    /// @notice Set the Report Bounds in storage
    /// @param _newReportBounds The new Report Bounds value
    function set(ReportBoundsStruct memory _newReportBounds) internal {
        bytes32 slot = REPORT_BOUNDS_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }

        r.value = _newReportBounds;
    }
}

File 23 of 25 : RiverAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibSanitize.sol";
import "../../libraries/LibUnstructuredStorage.sol";

/// @title River Address Storage
/// @notice Utility to manage the River Address in storage
library RiverAddress {
    /// @notice Storage slot of the River Address
    bytes32 internal constant RIVER_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.riverAddress")) - 1);

    /// @notice Retrieve the River Address
    /// @return The River Address
    function get() internal view returns (address) {
        return LibUnstructuredStorage.getStorageAddress(RIVER_ADDRESS_SLOT);
    }

    /// @notice Sets the River Address
    /// @param _newValue New River Address
    function set(address _newValue) internal {
        LibSanitize._notZeroAddress(_newValue);
        LibUnstructuredStorage.setStorageAddress(RIVER_ADDRESS_SLOT, _newValue);
    }
}

File 24 of 25 : Version.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibUnstructuredStorage.sol";

/// @title Version Storage
/// @notice Utility to manage the Version in storage
library Version {
    /// @notice Storage slot of the Version
    bytes32 public constant VERSION_SLOT = bytes32(uint256(keccak256("river.state.version")) - 1);

    /// @notice Retrieve the Version
    /// @return The Version
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(VERSION_SLOT);
    }

    /// @notice Sets the Version
    /// @param _newValue New Version
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(VERSION_SLOT, _newValue);
    }
}

File 25 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"rdata","type":"bytes"}],"name":"ClaimRedeemFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"redeemRequestId","type":"uint256"},{"internalType":"uint256","name":"withdrawalEventId","type":"uint256"}],"name":"DoesNotMatch","type":"error"},{"inputs":[],"name":"IncompatibleArrayLengths","type":"error"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"expectedVersion","type":"uint256"}],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"InvalidZeroAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"RedeemRequestAlreadyClaimed","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"RedeemRequestOutOfBounds","type":"error"},{"inputs":[],"name":"TransferError","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"WithdrawalEventOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"withdrawalAmount","type":"uint256"},{"internalType":"uint256","name":"redeemDemand","type":"uint256"}],"name":"WithdrawalExceedsRedeemDemand","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"redeemRequestId","type":"uint32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lsEthAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingLsEthAmount","type":"uint256"}],"name":"ClaimedRedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"height","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"}],"name":"ReportedWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"height","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRedeemableEth","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"}],"name":"RequestedRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"redeemRequestId","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"withdrawalEventId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"lsEthAmountSatisfied","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmountSatisfied","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lsEthAmountRemaining","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmountExceeding","type":"uint256"}],"name":"SatisfiedRedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldRedeemDemand","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRedeemDemand","type":"uint256"}],"name":"SetRedeemDemand","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"river","type":"address"}],"name":"SetRiver","type":"event"},{"inputs":[{"internalType":"uint32[]","name":"redeemRequestIds","type":"uint32[]"},{"internalType":"uint32[]","name":"withdrawalEventIds","type":"uint32[]"},{"internalType":"bool","name":"skipAlreadyClaimed","type":"bool"},{"internalType":"uint16","name":"_depth","type":"uint16"}],"name":"claimRedeemRequests","outputs":[{"internalType":"uint8[]","name":"claimStatuses","type":"uint8[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_redeemRequestIds","type":"uint32[]"},{"internalType":"uint32[]","name":"_withdrawalEventIds","type":"uint32[]"}],"name":"claimRedeemRequests","outputs":[{"internalType":"uint8[]","name":"claimStatuses","type":"uint8[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBufferedExceedingEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedeemDemand","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRedeemRequestCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_redeemRequestId","type":"uint32"}],"name":"getRedeemRequestDetails","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maxRedeemableEth","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct RedeemQueue.RedeemRequest","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawalEventCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_withdrawalEventId","type":"uint32"}],"name":"getWithdrawalEventDetails","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"withdrawnEth","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"internalType":"struct WithdrawalStack.WithdrawalEvent","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_river","type":"address"}],"name":"initializeRedeemManagerV1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max","type":"uint256"}],"name":"pullExceedingEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lsETHWithdrawable","type":"uint256"}],"name":"reportWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lsETHAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"requestRedeem","outputs":[{"internalType":"uint32","name":"redeemRequestId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lsETHAmount","type":"uint256"}],"name":"requestRedeem","outputs":[{"internalType":"uint32","name":"redeemRequestId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"_redeemRequestIds","type":"uint32[]"}],"name":"resolveRedeemRequests","outputs":[{"internalType":"int64[]","name":"withdrawalEventIds","type":"int64[]"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x6080604052600436106100b85760003560e01c80630c779401146100bd5780630d8d2a54146100df578063107703ab14610107578063319798d1146101345780635b5985a2146101495780635f2e5f071461015e5780637c044e551461018b578063841ecb85146101b857806386233754146101cd5780639332525d1461020f5780639b92d6de1461022f578063a519f0601461028b578063aa2f892d146102ad578063b30d8bb7146102cd578063c8fade5a146102e0575b600080fd5b3480156100c957600080fd5b506100dd6100d8366004611a92565b610300565b005b3480156100eb57600080fd5b506100f46103da565b6040519081526020015b60405180910390f35b34801561011357600080fd5b50610127610122366004611ac0565b6103e9565b6040516100fe9190611af0565b34801561014057600080fd5b506100f46104db565b34801561015557600080fd5b506100f46104eb565b34801561016a57600080fd5b5061017e610179366004611b4c565b6104f5565b6040516100fe9190611b8d565b34801561019757600080fd5b506101ab6101a6366004611be2565b61062a565b6040516100fe9190611c7d565b3480156101c457600080fd5b506100f4610645565b3480156101d957600080fd5b506101ed6101e8366004611cb8565b61064f565b60408051825181526020808401519082015291810151908201526060016100fe565b34801561021b57600080fd5b506101ab61022a366004611ce5565b6106b4565b34801561023b57600080fd5b5061024f61024a366004611cb8565b6106d0565b6040516100fe919081518152602080830151908201526040808301516001600160a01b0316908201526060918201519181019190915260800190565b34801561029757600080fd5b506102a0610747565b6040516100fe9190611d50565b3480156102b957600080fd5b506101276102c8366004611a92565b610751565b6100dd6102db366004611a92565b610829565b3480156102ec57600080fd5b506100dd6102fb366004611d64565b6109c5565b610308610abb565b6001600160a01b0316336001600160a01b031614610344573360405163472511eb60e11b815260040161033b9190611d50565b60405180910390fd5b6000610357610351610add565b83610afb565b905080156103d65761037a8161036b610add565b6103759190611d97565b610b10565b610382610747565b6001600160a01b031663056850c6826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156103bc57600080fd5b505af11580156103d0573d6000803e3d6000fd5b50505050505b5050565b60006103e4610b2c565b905090565b6000806103f4610747565b9050336001600160a01b038216146104c757806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610444573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104689190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b8152600401610496929190611dcb565b60006040518083038186803b1580156104ae57600080fd5b505afa1580156104c2573d6000803e3d6000fd5b505050505b506104d28383610b4a565b90505b92915050565b60006104e5610e08565b54919050565b60006103e4610add565b6060816001600160401b0381111561050f5761050f611de4565b604051908082528060200260200182016040528015610538578160200160208202803683370190505b5090506105436119e9565b600061054d610e36565b805490915080156105ae5781610564600183611d97565b8154811061057457610574611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b60005b85811015610620576105e98787838181106105ce576105ce611dfa565b90506020020160208101906105e39190611cb8565b85610e64565b8582815181106105fb576105fb611dfa565b602002602001019060070b908160070b815250508061061990611e10565b90506105b1565b5050505092915050565b606061063a878787878787610f46565b979650505050505050565b60006104e5610e36565b6106576119e9565b61065f610e36565b8263ffffffff168154811061067657610676611dfa565b906000526020600020906003020160405180606001604052908160008201548152602001600182015481526020016002820154815250509050919050565b60606106c785858585600161ffff610f46565b95945050505050565b6106d8611a0a565b6106e0610e08565b8263ffffffff16815481106106f7576106f7611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101549383019390935260028301546001600160a01b031690820152600390910154606082015292915050565b60006103e4610abb565b60008061075c610747565b9050806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c09190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b81526004016107ee929190611dcb565b60006040518083038186803b15801561080657600080fd5b505afa15801561081a573d6000803e3d6000fd5b50505050506104d58233610b4a565b610831610abb565b6001600160a01b0316336001600160a01b031614610864573360405163472511eb60e11b815260040161033b9190611d50565b600061086e610b2c565b90508082111561089b57604051632ea1a50d60e11b8152600481018390526024810182905260440161033b565b60006108a5610e36565b805490915060003463ffffffff831615610926576000846108c7600186611e2b565b63ffffffff16815481106108dd576108dd611dfa565b60009182526020918290206040805160608101825260039093029091018054808452600182015494840194909452600201549082018190529092506109229190611e50565b9250505b604080516060810182528781526020808201848152928201858152875460018181018a5560008a81529390932093516003909102909301928355925190820155905160029091015561098061097b8787611d97565b6113db565b7f05e5b10ca266c2e8cfd73b1fb719694fb6b89fcda9e3739ace4cc19b3e855445828783866040516109b59493929190611e68565b60405180910390a1505050505050565b60006109cf611424565b8114610a0057806109de611424565b604051631cfd276760e31b81526004810192909252602482015260440161033b565b610a13610a0e826001611e50565b610a93565b610a1c82611442565b7ffc0d749143b04e0d92a3b49edae84a45f6bc88b75e99d9118ec4d7e87fe626c582604051610a4b9190611d50565b60405180910390a17f1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a7181600036604051610a8793929190611e89565b60405180910390a15050565b610ab4610aaf60016000805160206120fd833981519152611d97565b829055565b50565b9055565b60006103e4610ad9600160008051602061209d833981519152611d97565b5490565b60006103e4610ad960016000805160206120bd833981519152611d97565b6000818311610b0a57826104d2565b50919050565b610ab4610aaf60016000805160206120bd833981519152611d97565b60006103e4610ad960016000805160206120dd833981519152611d97565b6000610b5582611467565b82610b7357604051630dd484e760e41b815260040160405180910390fd5b610b7b610747565b6040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b0391909116906323b872dd906064016020604051808303816000875af1158015610bd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf49190611ebf565b610c11576040516313ff771f60e21b815260040160405180910390fd5b6000610c1b610e08565b805492509050600063ffffffff831615610cb257600082610c3d600186611e2b565b63ffffffff1681548110610c5357610c53611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201819052909250610cae9190611e50565b9150505b6000610cbc610747565b6001600160a01b031663f79c3f02876040518263ffffffff1660e01b8152600401610ce991815260200190565b602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190611edc565b6040805160808101825288815260208082018481526001600160a01b038a811694840194855260608401888152895460018082018c5560008c8152959095209551600490910290950194855591519284019290925592516002830180546001600160a01b0319169190921617905590516003909101559050610db886610dae610b2c565b61097b9190611e50565b846001600160a01b03167f9a1bb960783a8679f42036f0c1fe89288fd279ed545a19f86bd284c72947b18783888488604051610df79493929190611e68565b60405180910390a250505092915050565b6000806104d560017f232f0d723a47bd57c991606dd0525f28484745095475b69dffe8416b3749c3c2611d97565b6000806104d560017f0c9122afab04eaf9e9bdaa10927c74406e40c7f08744e32826fff29b1c9e7abd611d97565b600080610e6f610e08565b805490915063ffffffff851610610e8b576001199150506104d5565b6000818563ffffffff1681548110610ea557610ea5611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201529150610f0357600219925050506104d5565b610f0b610e36565b541580610f2c5750606081015184516040860151610f299190611e50565b11155b15610f3d57600019925050506104d5565b6106c78161148e565b606085848114610f6957604051633a5e3f0d60e01b815260040160405180910390fd5b806001600160401b03811115610f8157610f81611de4565b604051908082528060200260200182016040528015610faa578160200160208202803683370190505b5091506000610fb7610e08565b90506000610fc3610e36565b9050610fcd611a3b565b815463ffffffff166080820152825460005b858110156113cb578c8c82818110610ff957610ff9611dfa565b905060200201602081019061100e9190611cb8565b63ffffffff1683528a8a8281811061102857611028611dfa565b905060200201602081019061103d9190611cb8565b63ffffffff90811660408501528351838216911610611072578251604051637ba6f8f560e11b815261033b9190600401611af0565b826080015163ffffffff16836040015163ffffffff16106110ac57826040015160405163311569d960e01b815260040161033b9190611af0565b848d8d838181106110bf576110bf611dfa565b90506020020160208101906110d49190611cb8565b63ffffffff16815481106110ea576110ea611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101548385015260028101546001600160a01b0316918301919091526003015460608201529084018190525161118d57881561117157600287828151811061115757611157611dfa565b60ff90921660209283029190910190910152600101610fdf565b82516040516339775c8160e01b815261033b9190600401611af0565b838b8b838181106111a0576111a0611dfa565b90506020020160208101906111b59190611cb8565b63ffffffff16815481106111cb576111cb611dfa565b600091825260209182902060408051606080820183526003909402909201805483526001810154838601526002015490820152908501819052908401516112119161160a565b611245578251604080850151905163b826420960e01b815263ffffffff92831660048201529116602482015260440161033b565b61ffff881660a0840152600060e0840181905260c08401526112668361163a565b6020830151511561127857600161127b565b60005b87828151811061128d5761128d611dfa565b602002602001019060ff16908160ff16815250506000808460200151604001516001600160a01b03168560e0015160405160006040518083038185875af1925050503d80600081146112fb576040519150601f19603f3d011682016040523d82523d6000602084013e611300565b606091505b50915091508161132f578460200151604001518160405163f2fc9fa160e01b815260040161033b929190611ef5565b50508260200151604001516001600160a01b03168d8d8381811061135557611355611dfa565b905060200201602081019061136a9190611cb8565b60e085015160c086015160208088015151604080519485529184019290925282015263ffffffff91909116907f25f4dfa5f0703d4c509bd7216e70f8378f419433c14840c14f3eaadb60642ad19060600160405180910390a3600101610fdf565b5050505050509695505050505050565b7f81ba2e33f1e91d5b498c91742740d1de8a6149f571c26bec9ae77b5cf099c582611404610b2c565b60408051918252602082018490520160405180910390a1610ab48161190f565b60006103e4610ad960016000805160206120fd833981519152611d97565b61144b81611467565b610ab4610aaf600160008051602061209d833981519152611d97565b6001600160a01b038116610ab45760405163f6b2911f60e01b815260040160405180910390fd5b600080611499610e36565b9050600060016114a7610e36565b546114b29190611d97565b905061150f8483836001600160401b0316815481106114d3576114d3611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505061160a565b1561151b579392505050565b600061153c8584836001600160401b0316815481106114d3576114d3611dfa565b1561154957949350505050565b8160070b8160070b1461160257600060026115648484611f5a565b61156e9190611fc1565b9050600084826001600160401b03168154811061158d5761158d611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505090506115d0878261160a565b156115df575095945050505050565b8060400151876060015110156115f7578193506115fb565b8192505b5050611549565b949350505050565b8051604082015160009161161d91611e50565b83606001511080156104d257505060400151606090910151101590565b61165e60405180606001604052806000815260200160008152602001600081525090565b6060820151805160409091015160009161167791611e50565b6020840151805160609091015191925061169a916116959084611d97565b610afb565b60208084018290526060850151805191015190916116b89190611fff565b6116c2919061201e565b8252602080840151805190820151918401516000926116e091611fff565b6116ea919061201e565b8351909150811015611724578251611703908290611d97565b6040840181905261172090611716610add565b6103759190611e50565b8083525b8260200151846020015160600181815161173e9190611e50565b905250602080840151908501518051611758908390611d97565b9052508251602080860151018051611771908390611d97565b905250602083015160c08501805161178a908390611e50565b905250825160e0850180516117a0908390611e50565b90525060408481015185516020868101518751828a0151518987015187519384529383019190915294810194909452606084015263ffffffff918216929116907f01d87b0f07b182b784c074487c5a8f901a8e2348275f5480d822a70de74fa8b79060800160405180910390a35050602082015151158015906118425750816080015163ffffffff168260400151600161183a9190612032565b63ffffffff16105b8015611856575060008260a0015161ffff16115b15611906576000611865610e36565b90508260400180516118769061205a565b63ffffffff16908163ffffffff168152505080836040015163ffffffff16815481106118a4576118a4611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505083606001819052508260a00180516118f29061207e565b61ffff1690526119018361163a565b505050565b6103d68261192b565b610ab4610aaf60016000805160206120dd833981519152611d97565b6000611935610e08565b905081602001516060015181836000015163ffffffff168154811061195c5761195c611dfa565b90600052602060002090600402016003018190555081602001516000015181836000015163ffffffff168154811061199657611996611dfa565b90600052602060002090600402016000018190555081602001516020015181836000015163ffffffff16815481106119d0576119d0611dfa565b9060005260206000209060040201600101819055505050565b60405180606001604052806000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b604051806101000160405280600063ffffffff168152602001611a5c611a0a565b815260006020820152604001611a706119e9565b8152600060208201819052604082018190526060820181905260809091015290565b600060208284031215611aa457600080fd5b5035919050565b6001600160a01b0381168114610ab457600080fd5b60008060408385031215611ad357600080fd5b823591506020830135611ae581611aab565b809150509250929050565b63ffffffff91909116815260200190565b60008083601f840112611b1357600080fd5b5081356001600160401b03811115611b2a57600080fd5b6020830191508360208260051b8501011115611b4557600080fd5b9250929050565b60008060208385031215611b5f57600080fd5b82356001600160401b03811115611b7557600080fd5b611b8185828601611b01565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160070b83529284019291840191600101611ba9565b50909695505050505050565b8015158114610ab457600080fd5b60008060008060008060808789031215611bfb57600080fd5b86356001600160401b0380821115611c1257600080fd5b611c1e8a838b01611b01565b90985096506020890135915080821115611c3757600080fd5b50611c4489828a01611b01565b9095509350506040870135611c5881611bd4565b9150606087013561ffff81168114611c6f57600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160ff1683529284019291840191600101611c99565b600060208284031215611cca57600080fd5b813563ffffffff81168114611cde57600080fd5b9392505050565b60008060008060408587031215611cfb57600080fd5b84356001600160401b0380821115611d1257600080fd5b611d1e88838901611b01565b90965094506020870135915080821115611d3757600080fd5b50611d4487828801611b01565b95989497509550505050565b6001600160a01b0391909116815260200190565b600060208284031215611d7657600080fd5b8135611cde81611aab565b634e487b7160e01b600052601160045260246000fd5b600082821015611da957611da9611d81565b500390565b600060208284031215611dc057600080fd5b8151611cde81611aab565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415611e2457611e24611d81565b5060010190565b600063ffffffff83811690831681811015611e4857611e48611d81565b039392505050565b60008219821115611e6357611e63611d81565b500190565b9384526020840192909252604083015263ffffffff16606082015260800190565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b600060208284031215611ed157600080fd5b8151611cde81611bd4565b600060208284031215611eee57600080fd5b5051919050565b60018060a01b038316815260006020604081840152835180604085015260005b81811015611f3157858101830151858201606001528201611f15565b81811115611f43576000606083870101525b50601f01601f191692909201606001949350505050565b6000600782810b9084900b828212801560016001603f1b0384900383131615611f8557611f85611d81565b60016001603f1b03198390038212811615611fa257611fa2611d81565b50019392505050565b634e487b7160e01b600052601260045260246000fd5b60008160070b8360070b80611fd857611fd8611fab565b60016001603f1b0319821460001982141615611ff657611ff6611d81565b90059392505050565b600081600019048311821515161561201957612019611d81565b500290565b60008261202d5761202d611fab565b500490565b600063ffffffff80831681851680830382111561205157612051611d81565b01949350505050565b600063ffffffff8083168181141561207457612074611d81565b6001019392505050565b600061ffff82168061209257612092611d81565b600019019291505056fe1ec4138404500a2a0be2c2f9b103581c2a7fa783a934f91a6cc5cc924404973c9afb091d33aa9fdd4ac2dbd1cbba78215a097062e9dbdc7b707ac5bb59049bc18ec604dcc41d380ed48f70a4a32dd9623b98c284836361a21b8992d2845ee53d82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfda2646970667358221220217c4fd0cfc891004dde0e311f7c7606db72a075c7156051d3cbb7d569f9775664736f6c634300080a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.