ETH Price: $2,408.28 (-0.26%)

Contract

0x423CE5282c460EED5FE0786B4D47d2c2a4Ef3721
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040182836742023-10-05 10:20:11346 days ago1696501211IN
 Create: RedeemManagerV1
0 ETH0.012488476.49307425

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RedeemManagerV1

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 10 runs

Other Settings:
default evmVersion, BSL 1.1 license
File 1 of 116 : 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 116 : Administrable.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IAdministrable.sol";

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

/// @title Administrable
/// @author Kiln
/// @notice This contract handles the administration of the contracts
abstract contract Administrable is IAdministrable {
    /// @notice Prevents unauthorized calls
    modifier onlyAdmin() {
        if (msg.sender != LibAdministrable._getAdmin()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @notice Prevents unauthorized calls
    modifier onlyPendingAdmin() {
        if (msg.sender != LibAdministrable._getPendingAdmin()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @inheritdoc IAdministrable
    function getAdmin() external view returns (address) {
        return LibAdministrable._getAdmin();
    }

    /// @inheritdoc IAdministrable
    function getPendingAdmin() external view returns (address) {
        return LibAdministrable._getPendingAdmin();
    }

    /// @inheritdoc IAdministrable
    function proposeAdmin(address _newAdmin) external onlyAdmin {
        _setPendingAdmin(_newAdmin);
    }

    /// @inheritdoc IAdministrable
    function acceptAdmin() external onlyPendingAdmin {
        _setAdmin(LibAdministrable._getPendingAdmin());
        _setPendingAdmin(address(0));
    }

    /// @notice Internal utility to set the admin address
    /// @param _admin Address to set as admin
    function _setAdmin(address _admin) internal {
        LibSanitize._notZeroAddress(_admin);
        LibAdministrable._setAdmin(_admin);
        emit SetAdmin(_admin);
    }

    /// @notice Internal utility to set the pending admin address
    /// @param _pendingAdmin Address to set as pending admin
    function _setPendingAdmin(address _pendingAdmin) internal {
        LibAdministrable._setPendingAdmin(_pendingAdmin);
        emit SetPendingAdmin(_pendingAdmin);
    }

    /// @notice Internal utility to retrieve the address of the current admin
    /// @return The address of admin
    function _getAdmin() internal view returns (address) {
        return LibAdministrable._getAdmin();
    }
}

File 3 of 116 : IAdministrable.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Administrable Interface
/// @author Kiln
/// @notice This interface exposes methods to handle the ownership of the contracts
interface IAdministrable {
    /// @notice The pending admin address changed
    /// @param pendingAdmin New pending admin address
    event SetPendingAdmin(address indexed pendingAdmin);

    /// @notice The admin address changed
    /// @param admin New admin address
    event SetAdmin(address indexed admin);

    /// @notice Retrieves the current admin address
    /// @return The admin address
    function getAdmin() external view returns (address);

    /// @notice Retrieve the current pending admin address
    /// @return The pending admin address
    function getPendingAdmin() external view returns (address);

    /// @notice Proposes a new address as admin
    /// @dev This security prevents setting an invalid address as an admin. The pending
    /// @dev admin has to claim its ownership of the contract, and prove that the new
    /// @dev address is able to perform regular transactions.
    /// @param _newAdmin New admin address
    function proposeAdmin(address _newAdmin) external;

    /// @notice Accept the transfer of ownership
    /// @dev Only callable by the pending admin. Resets the pending admin if succesful.
    function acceptAdmin() external;
}

File 4 of 116 : LibAdministrable.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "../state/shared/AdministratorAddress.sol";
import "../state/shared/PendingAdministratorAddress.sol";

/// @title Lib Administrable
/// @author Kiln
/// @notice This library handles the admin and pending admin storage vars
library LibAdministrable {
    /// @notice Retrieve the system admin
    /// @return The address of the system admin
    function _getAdmin() internal view returns (address) {
        return AdministratorAddress.get();
    }

    /// @notice Retrieve the pending system admin
    /// @return The adress of the pending system admin
    function _getPendingAdmin() internal view returns (address) {
        return PendingAdministratorAddress.get();
    }

    /// @notice Sets the system admin
    /// @param _admin New system admin
    function _setAdmin(address _admin) internal {
        AdministratorAddress.set(_admin);
    }

    /// @notice Sets the pending system admin
    /// @param _pendingAdmin New pending system admin
    function _setPendingAdmin(address _pendingAdmin) internal {
        PendingAdministratorAddress.set(_pendingAdmin);
    }
}

File 5 of 116 : 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 6 of 116 : AdministratorAddress.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

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

/// @title Administrator Address Storage
/// @notice Utility to manage the Administrator Address in storage
library AdministratorAddress {
    /// @notice Storage slot of the Administrator Address
    bytes32 public constant ADMINISTRATOR_ADDRESS_SLOT =
        bytes32(uint256(keccak256("river.state.administratorAddress")) - 1);

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

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

File 7 of 116 : PendingAdministratorAddress.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

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

/// @title Pending Administrator Address Storage
/// @notice Utility to manage the Pending Administrator Address in storage
library PendingAdministratorAddress {
    /// @notice Storage slot of the Pending Administrator Address
    bytes32 public constant PENDING_ADMINISTRATOR_ADDRESS_SLOT =
        bytes32(uint256(keccak256("river.state.pendingAdministratorAddress")) - 1);

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

    /// @notice Sets the Pending Administrator Address
    /// @param _newValue New Pending Administrator Address
    function set(address _newValue) internal {
        LibUnstructuredStorage.setStorageAddress(PENDING_ADMINISTRATOR_ADDRESS_SLOT, _newValue);
    }
}

File 8 of 116 : 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 9 of 116 : 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 10 of 116 : 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 11 of 116 : River.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IAllowlist.1.sol";
import "./interfaces/IOperatorRegistry.1.sol";
import "./interfaces/IRiver.1.sol";
import "./interfaces/IWithdraw.1.sol";
import "./interfaces/IELFeeRecipient.1.sol";
import "./interfaces/ICoverageFund.1.sol";

import "./components/ConsensusLayerDepositManager.1.sol";
import "./components/UserDepositManager.1.sol";
import "./components/SharesManager.1.sol";
import "./components/OracleManager.1.sol";
import "./Initializable.sol";
import "./Administrable.sol";

import "./libraries/LibAllowlistMasks.sol";

import "./state/river/AllowlistAddress.sol";
import "./state/river/RedeemManagerAddress.sol";
import "./state/river/OperatorsRegistryAddress.sol";
import "./state/river/CollectorAddress.sol";
import "./state/river/ELFeeRecipientAddress.sol";
import "./state/river/CoverageFundAddress.sol";
import "./state/river/BalanceToRedeem.sol";
import "./state/river/GlobalFee.sol";
import "./state/river/MetadataURI.sol";
import "./state/river/LastConsensusLayerReport.sol";

/// @title River (v1)
/// @author Kiln
/// @notice This contract merges all the manager contracts and implements all the virtual methods stitching all components together
contract RiverV1 is
    ConsensusLayerDepositManagerV1,
    UserDepositManagerV1,
    SharesManagerV1,
    OracleManagerV1,
    Initializable,
    Administrable,
    IRiverV1
{
    /// @inheritdoc IRiverV1
    function initRiverV1(
        address _depositContractAddress,
        address _elFeeRecipientAddress,
        bytes32 _withdrawalCredentials,
        address _oracleAddress,
        address _systemAdministratorAddress,
        address _allowlistAddress,
        address _operatorRegistryAddress,
        address _collectorAddress,
        uint256 _globalFee
    ) external init(0) {
        _setAdmin(_systemAdministratorAddress);

        CollectorAddress.set(_collectorAddress);
        emit SetCollector(_collectorAddress);

        GlobalFee.set(_globalFee);
        emit SetGlobalFee(_globalFee);

        ELFeeRecipientAddress.set(_elFeeRecipientAddress);
        emit SetELFeeRecipient(_elFeeRecipientAddress);

        AllowlistAddress.set(_allowlistAddress);
        emit SetAllowlist(_allowlistAddress);

        OperatorsRegistryAddress.set(_operatorRegistryAddress);
        emit SetOperatorsRegistry(_operatorRegistryAddress);

        ConsensusLayerDepositManagerV1.initConsensusLayerDepositManagerV1(
            _depositContractAddress, _withdrawalCredentials
        );

        OracleManagerV1.initOracleManagerV1(_oracleAddress);
    }

    /// @inheritdoc IRiverV1
    function initRiverV1_1(
        address _redeemManager,
        uint64 _epochsPerFrame,
        uint64 _slotsPerEpoch,
        uint64 _secondsPerSlot,
        uint64 _genesisTime,
        uint64 _epochsToAssumedFinality,
        uint256 _annualAprUpperBound,
        uint256 _relativeLowerBound,
        uint128 _minDailyNetCommittableAmount_,
        uint128 _maxDailyRelativeCommittableAmount_
    ) external init(1) {
        RedeemManagerAddress.set(_redeemManager);
        emit SetRedeemManager(_redeemManager);

        _setDailyCommittableLimits(
            DailyCommittableLimits.DailyCommittableLimitsStruct({
                minDailyNetCommittableAmount: _minDailyNetCommittableAmount_,
                maxDailyRelativeCommittableAmount: _maxDailyRelativeCommittableAmount_
            })
        );

        initOracleManagerV1_1(
            _epochsPerFrame,
            _slotsPerEpoch,
            _secondsPerSlot,
            _genesisTime,
            _epochsToAssumedFinality,
            _annualAprUpperBound,
            _relativeLowerBound
        );

        _approve(address(this), _redeemManager, type(uint256).max);
    }

    /// @inheritdoc IRiverV1
    function initRiverV1_2() external init(2) {
        // force committed balance to a multiple of 32 ETH and
        // move extra funds back to the deposit buffer
        uint256 dustToUncommit = CommittedBalance.get() % DEPOSIT_SIZE;
        _setCommittedBalance(CommittedBalance.get() - dustToUncommit);
        _setBalanceToDeposit(BalanceToDeposit.get() + dustToUncommit);
    }

    /// @inheritdoc IRiverV1
    function getGlobalFee() external view returns (uint256) {
        return GlobalFee.get();
    }

    /// @inheritdoc IRiverV1
    function getAllowlist() external view returns (address) {
        return AllowlistAddress.get();
    }

    /// @inheritdoc IRiverV1
    function getCollector() external view returns (address) {
        return CollectorAddress.get();
    }

    /// @inheritdoc IRiverV1
    function getELFeeRecipient() external view returns (address) {
        return ELFeeRecipientAddress.get();
    }

    /// @inheritdoc IRiverV1
    function getCoverageFund() external view returns (address) {
        return CoverageFundAddress.get();
    }

    /// @inheritdoc IRiverV1
    function getRedeemManager() external view returns (address) {
        return RedeemManagerAddress.get();
    }

    /// @inheritdoc IRiverV1
    function getMetadataURI() external view returns (string memory) {
        return MetadataURI.get();
    }

    /// @inheritdoc IRiverV1
    function getDailyCommittableLimits()
        external
        view
        returns (DailyCommittableLimits.DailyCommittableLimitsStruct memory)
    {
        return DailyCommittableLimits.get();
    }

    /// @inheritdoc IRiverV1
    function setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl)
        external
        onlyAdmin
    {
        _setDailyCommittableLimits(_dcl);
    }

    /// @inheritdoc IRiverV1
    function getBalanceToRedeem() external view returns (uint256) {
        return BalanceToRedeem.get();
    }

    /// @inheritdoc IRiverV1
    function resolveRedeemRequests(uint32[] calldata _redeemRequestIds)
        external
        view
        returns (int64[] memory withdrawalEventIds)
    {
        return IRedeemManagerV1(RedeemManagerAddress.get()).resolveRedeemRequests(_redeemRequestIds);
    }

    /// @inheritdoc IRiverV1
    function requestRedeem(uint256 _lsETHAmount, address _recipient) external returns (uint32 _redeemRequestId) {
        IAllowlistV1(AllowlistAddress.get()).onlyAllowed(msg.sender, LibAllowlistMasks.REDEEM_MASK);
        _transfer(msg.sender, address(this), _lsETHAmount);
        return IRedeemManagerV1(RedeemManagerAddress.get()).requestRedeem(_lsETHAmount, _recipient);
    }

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

    /// @inheritdoc IRiverV1
    function setGlobalFee(uint256 _newFee) external onlyAdmin {
        GlobalFee.set(_newFee);
        emit SetGlobalFee(_newFee);
    }

    /// @inheritdoc IRiverV1
    function setAllowlist(address _newAllowlist) external onlyAdmin {
        AllowlistAddress.set(_newAllowlist);
        emit SetAllowlist(_newAllowlist);
    }

    /// @inheritdoc IRiverV1
    function setCollector(address _newCollector) external onlyAdmin {
        CollectorAddress.set(_newCollector);
        emit SetCollector(_newCollector);
    }

    /// @inheritdoc IRiverV1
    function setELFeeRecipient(address _newELFeeRecipient) external onlyAdmin {
        ELFeeRecipientAddress.set(_newELFeeRecipient);
        emit SetELFeeRecipient(_newELFeeRecipient);
    }

    /// @inheritdoc IRiverV1
    function setCoverageFund(address _newCoverageFund) external onlyAdmin {
        CoverageFundAddress.set(_newCoverageFund);
        emit SetCoverageFund(_newCoverageFund);
    }

    /// @inheritdoc IRiverV1
    function setMetadataURI(string memory _metadataURI) external onlyAdmin {
        LibSanitize._notEmptyString(_metadataURI);
        MetadataURI.set(_metadataURI);
        emit SetMetadataURI(_metadataURI);
    }

    /// @inheritdoc IRiverV1
    function getOperatorsRegistry() external view returns (address) {
        return OperatorsRegistryAddress.get();
    }

    /// @inheritdoc IRiverV1
    function sendELFees() external payable {
        if (msg.sender != ELFeeRecipientAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
    }

    /// @inheritdoc IRiverV1
    function sendCLFunds() external payable {
        if (msg.sender != WithdrawalCredentials.getAddress()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
    }

    /// @inheritdoc IRiverV1
    function sendCoverageFunds() external payable {
        if (msg.sender != CoverageFundAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
    }

    /// @inheritdoc IRiverV1
    function sendRedeemManagerExceedingFunds() external payable {
        if (msg.sender != RedeemManagerAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
    }

    /// @notice Overridden handler to pass the system admin inside components
    /// @return The address of the admin
    function _getRiverAdmin()
        internal
        view
        override(OracleManagerV1, ConsensusLayerDepositManagerV1)
        returns (address)
    {
        return Administrable._getAdmin();
    }

    /// @notice Overridden handler called whenever a token transfer is triggered
    /// @param _from Token sender
    /// @param _to Token receiver
    function _onTransfer(address _from, address _to) internal view override {
        IAllowlistV1 allowlist = IAllowlistV1(AllowlistAddress.get());
        if (allowlist.isDenied(_from)) {
            revert Denied(_from);
        }
        if (allowlist.isDenied(_to)) {
            revert Denied(_to);
        }
    }

    /// @notice Overridden handler called whenever a user deposits ETH to the system. Mints the adequate amount of shares.
    /// @param _depositor User address that made the deposit
    /// @param _amount Amount of ETH deposited
    function _onDeposit(address _depositor, address _recipient, uint256 _amount) internal override {
        uint256 mintedShares = SharesManagerV1._mintShares(_depositor, _amount);
        IAllowlistV1 allowlist = IAllowlistV1(AllowlistAddress.get());
        if (_depositor == _recipient) {
            allowlist.onlyAllowed(_depositor, LibAllowlistMasks.DEPOSIT_MASK); // this call reverts if unauthorized or denied
        } else {
            allowlist.onlyAllowed(_depositor, LibAllowlistMasks.DEPOSIT_MASK); // this call reverts if unauthorized or denied
            if (allowlist.isDenied(_recipient)) {
                revert Denied(_recipient);
            }
            _transfer(_depositor, _recipient, mintedShares);
        }
    }

    /// @notice Overridden handler called whenever a deposit to the consensus layer is made. Should retrieve _requestedAmount or lower keys
    /// @param _requestedAmount Amount of keys required. Contract is expected to send _requestedAmount or lower.
    /// @return publicKeys Array of fundable public keys
    /// @return signatures Array of signatures linked to the public keys
    function _getNextValidators(uint256 _requestedAmount)
        internal
        override
        returns (bytes[] memory publicKeys, bytes[] memory signatures)
    {
        return IOperatorsRegistryV1(OperatorsRegistryAddress.get()).pickNextValidatorsToDeposit(_requestedAmount);
    }

    /// @notice Overridden handler to pull funds from the execution layer fee recipient to River and return the delta in the balance
    /// @param _max The maximum amount to pull from the execution layer fee recipient
    /// @return The amount pulled from the execution layer fee recipient
    function _pullELFees(uint256 _max) internal override returns (uint256) {
        address elFeeRecipient = ELFeeRecipientAddress.get();
        uint256 initialBalance = address(this).balance;
        IELFeeRecipientV1(payable(elFeeRecipient)).pullELFees(_max);
        uint256 collectedELFees = address(this).balance - initialBalance;
        if (collectedELFees > 0) {
            _setBalanceToDeposit(BalanceToDeposit.get() + collectedELFees);
        }
        emit PulledELFees(collectedELFees);
        return collectedELFees;
    }

    /// @notice Overridden handler to pull funds from the coverage fund to River and return the delta in the balance
    /// @param _max The maximum amount to pull from the coverage fund
    /// @return The amount pulled from the coverage fund
    function _pullCoverageFunds(uint256 _max) internal override returns (uint256) {
        address coverageFund = CoverageFundAddress.get();
        if (coverageFund == address(0)) {
            return 0;
        }
        uint256 initialBalance = address(this).balance;
        ICoverageFundV1(payable(coverageFund)).pullCoverageFunds(_max);
        uint256 collectedCoverageFunds = address(this).balance - initialBalance;
        if (collectedCoverageFunds > 0) {
            _setBalanceToDeposit(BalanceToDeposit.get() + collectedCoverageFunds);
        }
        emit PulledCoverageFunds(collectedCoverageFunds);
        return collectedCoverageFunds;
    }

    /// @notice Overridden handler called whenever the balance of ETH handled by the system increases. Computes the fees paid to the collector
    /// @param _amount Additional ETH received
    function _onEarnings(uint256 _amount) internal override {
        uint256 oldTotalSupply = _totalSupply();
        if (oldTotalSupply == 0) {
            revert ZeroMintedShares();
        }
        uint256 newTotalBalance = _assetBalance();
        uint256 globalFee = GlobalFee.get();
        uint256 numerator = _amount * oldTotalSupply * globalFee;
        uint256 denominator = (newTotalBalance * LibBasisPoints.BASIS_POINTS_MAX) - (_amount * globalFee);
        uint256 sharesToMint = denominator == 0 ? 0 : (numerator / denominator);

        if (sharesToMint > 0) {
            address collector = CollectorAddress.get();
            _mintRawShares(collector, sharesToMint);
            uint256 newTotalSupply = _totalSupply();
            uint256 oldTotalBalance = newTotalBalance - _amount;
            emit RewardsEarned(collector, oldTotalBalance, oldTotalSupply, newTotalBalance, newTotalSupply);
        }
    }

    /// @notice Overridden handler called whenever the total balance of ETH is requested
    /// @return The current total asset balance managed by River
    function _assetBalance() internal view override(SharesManagerV1, OracleManagerV1) returns (uint256) {
        IOracleManagerV1.StoredConsensusLayerReport storage storedReport = LastConsensusLayerReport.get();
        uint256 clValidatorCount = storedReport.validatorsCount;
        uint256 depositedValidatorCount = DepositedValidatorCount.get();
        if (clValidatorCount < depositedValidatorCount) {
            return storedReport.validatorsBalance + BalanceToDeposit.get() + CommittedBalance.get()
                + BalanceToRedeem.get()
                + (depositedValidatorCount - clValidatorCount) * ConsensusLayerDepositManagerV1.DEPOSIT_SIZE;
        } else {
            return
                storedReport.validatorsBalance + BalanceToDeposit.get() + CommittedBalance.get() + BalanceToRedeem.get();
        }
    }

    /// @notice Internal utility to set the daily committable limits
    /// @param _dcl The new daily committable limits
    function _setDailyCommittableLimits(DailyCommittableLimits.DailyCommittableLimitsStruct memory _dcl) internal {
        DailyCommittableLimits.set(_dcl);
        emit SetMaxDailyCommittableAmounts(_dcl.minDailyNetCommittableAmount, _dcl.maxDailyRelativeCommittableAmount);
    }

    /// @notice Sets the balance to deposit, but not yet committed
    /// @param _newBalanceToDeposit The new balance to deposit value
    function _setBalanceToDeposit(uint256 _newBalanceToDeposit) internal override(UserDepositManagerV1) {
        emit SetBalanceToDeposit(BalanceToDeposit.get(), _newBalanceToDeposit);
        BalanceToDeposit.set(_newBalanceToDeposit);
    }

    /// @notice Sets the balance to redeem, to be used to satisfy redeem requests on the redeem manager
    /// @param _newBalanceToRedeem The new balance to redeem value
    function _setBalanceToRedeem(uint256 _newBalanceToRedeem) internal {
        emit SetBalanceToRedeem(BalanceToRedeem.get(), _newBalanceToRedeem);
        BalanceToRedeem.set(_newBalanceToRedeem);
    }

    /// @notice Sets the committed balance, ready to be deposited to the consensus layer
    /// @param _newCommittedBalance The new committed balance value
    function _setCommittedBalance(uint256 _newCommittedBalance) internal override(ConsensusLayerDepositManagerV1) {
        emit SetBalanceCommittedToDeposit(CommittedBalance.get(), _newCommittedBalance);
        CommittedBalance.set(_newCommittedBalance);
    }

    /// @notice Pulls funds from the Withdraw contract, and adds funds to deposit and redeem balances
    /// @param _skimmedEthAmount The new amount of skimmed eth to pull
    /// @param _exitedEthAmount The new amount of exited eth to pull
    function _pullCLFunds(uint256 _skimmedEthAmount, uint256 _exitedEthAmount) internal override {
        uint256 currentBalance = address(this).balance;
        uint256 totalAmountToPull = _skimmedEthAmount + _exitedEthAmount;
        IWithdrawV1(WithdrawalCredentials.getAddress()).pullEth(totalAmountToPull);
        uint256 collectedCLFunds = address(this).balance - currentBalance;
        if (collectedCLFunds != _skimmedEthAmount + _exitedEthAmount) {
            revert InvalidPulledClFundsAmount(_skimmedEthAmount + _exitedEthAmount, collectedCLFunds);
        }
        if (_skimmedEthAmount > 0) {
            _setBalanceToDeposit(BalanceToDeposit.get() + _skimmedEthAmount);
        }
        if (_exitedEthAmount > 0) {
            _setBalanceToRedeem(BalanceToRedeem.get() + _exitedEthAmount);
        }
        emit PulledCLFunds(_skimmedEthAmount, _exitedEthAmount);
    }

    /// @notice Pulls funds from the redeem manager exceeding eth buffer
    /// @param _max The maximum amount to pull
    function _pullRedeemManagerExceedingEth(uint256 _max) internal override returns (uint256) {
        uint256 currentBalance = address(this).balance;
        IRedeemManagerV1(RedeemManagerAddress.get()).pullExceedingEth(_max);
        uint256 collectedExceedingEth = address(this).balance - currentBalance;
        if (collectedExceedingEth > 0) {
            _setBalanceToDeposit(BalanceToDeposit.get() + collectedExceedingEth);
        }
        emit PulledRedeemManagerExceedingEth(collectedExceedingEth);
        return collectedExceedingEth;
    }

    /// @notice Use the balance to redeem to report a withdrawal event on the redeem manager
    function _reportWithdrawToRedeemManager() internal override {
        IRedeemManagerV1 redeemManager_ = IRedeemManagerV1(RedeemManagerAddress.get());
        uint256 underlyingAssetBalance = _assetBalance();
        uint256 totalSupply = _totalSupply();

        if (underlyingAssetBalance > 0 && totalSupply > 0) {
            // we compute the redeem manager demands in eth and lsEth based on current conversion rate
            uint256 redeemManagerDemand = redeemManager_.getRedeemDemand();
            uint256 suppliedRedeemManagerDemand = redeemManagerDemand;
            uint256 suppliedRedeemManagerDemandInEth = _balanceFromShares(suppliedRedeemManagerDemand);
            uint256 availableBalanceToRedeem = BalanceToRedeem.get();

            // if demand is higher than available eth, we update demand values to use the available eth
            if (suppliedRedeemManagerDemandInEth > availableBalanceToRedeem) {
                suppliedRedeemManagerDemandInEth = availableBalanceToRedeem;
                suppliedRedeemManagerDemand = _sharesFromBalance(suppliedRedeemManagerDemandInEth);
            }

            emit ReportedRedeemManager(
                redeemManagerDemand, suppliedRedeemManagerDemand, suppliedRedeemManagerDemandInEth
            );

            if (suppliedRedeemManagerDemandInEth > 0) {
                // the available balance to redeem is updated
                _setBalanceToRedeem(availableBalanceToRedeem - suppliedRedeemManagerDemandInEth);

                // we burn the shares of the redeem manager associated with the amount of eth provided
                _burnRawShares(address(redeemManager_), suppliedRedeemManagerDemand);

                // perform a report withdraw call to the redeem manager
                redeemManager_.reportWithdraw{value: suppliedRedeemManagerDemandInEth}(suppliedRedeemManagerDemand);
            }
        }
    }

    /// @notice Requests exits of validators after possibly rebalancing deposit and redeem balances
    /// @param _exitingBalance The currently exiting funds, soon to be received on the execution layer
    /// @param _depositToRedeemRebalancingAllowed True if rebalancing from deposit to redeem is allowed
    function _requestExitsBasedOnRedeemDemandAfterRebalancings(
        uint256 _exitingBalance,
        uint32[] memory _stoppedValidatorCounts,
        bool _depositToRedeemRebalancingAllowed,
        bool _slashingContainmentModeEnabled
    ) internal override {
        IOperatorsRegistryV1(OperatorsRegistryAddress.get()).reportStoppedValidatorCounts(
            _stoppedValidatorCounts, DepositedValidatorCount.get()
        );

        if (_slashingContainmentModeEnabled) {
            return;
        }

        uint256 totalSupply = _totalSupply();
        if (totalSupply > 0) {
            uint256 availableBalanceToRedeem = BalanceToRedeem.get();
            uint256 availableBalanceToDeposit = BalanceToDeposit.get();
            uint256 redeemManagerDemandInEth =
                _balanceFromShares(IRedeemManagerV1(RedeemManagerAddress.get()).getRedeemDemand());

            // if after all rebalancings, the redeem manager demand is still higher than the balance to redeem and exiting eth, we compute
            // the amount of validators to exit in order to cover the remaining demand
            if (availableBalanceToRedeem + _exitingBalance < redeemManagerDemandInEth) {
                // if reblancing is enabled and the redeem manager demand is higher than exiting eth, we add eth for deposit buffer to redeem buffer
                if (_depositToRedeemRebalancingAllowed && availableBalanceToDeposit > 0) {
                    uint256 rebalancingAmount = LibUint256.min(
                        availableBalanceToDeposit, redeemManagerDemandInEth - _exitingBalance - availableBalanceToRedeem
                    );
                    if (rebalancingAmount > 0) {
                        availableBalanceToRedeem += rebalancingAmount;
                        _setBalanceToRedeem(availableBalanceToRedeem);
                        _setBalanceToDeposit(availableBalanceToDeposit - rebalancingAmount);
                    }
                }

                IOperatorsRegistryV1 or = IOperatorsRegistryV1(OperatorsRegistryAddress.get());

                (uint256 totalStoppedValidatorCount, uint256 totalRequestedExitsCount) =
                    or.getStoppedAndRequestedExitCounts();

                // what we are calling pre-exiting balance is the amount of eth that should soon enter the exiting balance
                // because exit requests have been made and operators might have a lag to process them
                // we take them into account to not exit too many validators
                uint256 preExitingBalance = (
                    totalRequestedExitsCount > totalStoppedValidatorCount
                        ? (totalRequestedExitsCount - totalStoppedValidatorCount)
                        : 0
                ) * DEPOSIT_SIZE;

                if (availableBalanceToRedeem + _exitingBalance + preExitingBalance < redeemManagerDemandInEth) {
                    uint256 validatorCountToExit = LibUint256.ceil(
                        redeemManagerDemandInEth - (availableBalanceToRedeem + _exitingBalance + preExitingBalance),
                        DEPOSIT_SIZE
                    );

                    or.demandValidatorExits(validatorCountToExit, DepositedValidatorCount.get());
                }
            }
        }
    }

    /// @notice Skims the redeem balance and sends remaining funds to the deposit balance
    function _skimExcessBalanceToRedeem() internal override {
        uint256 availableBalanceToRedeem = BalanceToRedeem.get();

        // if the available balance to redeem is not 0, it means that all the redeem requests are fulfilled, we should redirect funds for deposits
        if (availableBalanceToRedeem > 0) {
            _setBalanceToDeposit(BalanceToDeposit.get() + availableBalanceToRedeem);
            _setBalanceToRedeem(0);
        }
    }

    /// @notice Commits the deposit balance up to the allowed daily limit in batches of 32 ETH.
    /// @notice Committed funds are funds waiting to be deposited but that cannot be used to fund the redeem manager anymore
    /// @notice This two step process is required to prevent possible out of gas issues we would have from actually funding the validators at this point
    /// @param _period The period between current and last report
    function _commitBalanceToDeposit(uint256 _period) internal override {
        uint256 underlyingAssetBalance = _assetBalance();
        uint256 currentBalanceToDeposit = BalanceToDeposit.get();
        DailyCommittableLimits.DailyCommittableLimitsStruct memory dcl = DailyCommittableLimits.get();

        // we compute the max daily committable amount by taking the asset balance without the balance to deposit into account
        // this value is the daily maximum amount we can commit for deposits
        // we take the maximum value between a net amount and an amount relative to the asset balance
        // this ensures that the amount we can commit is not too low in the beginning and that it is not too high when volumes grow
        // the relative amount is computed from the committed and activated funds (on the CL or committed to be on the CL soon) and not
        // the deposit balance
        // this value is computed by subtracting the current balance to deposit from the underlying asset balance
        uint256 currentMaxDailyCommittableAmount = LibUint256.max(
            dcl.minDailyNetCommittableAmount,
            (uint256(dcl.maxDailyRelativeCommittableAmount) * (underlyingAssetBalance - currentBalanceToDeposit))
                / LibBasisPoints.BASIS_POINTS_MAX
        );
        // we adapt the value for the reporting period by using the asset balance as upper bound
        uint256 currentMaxCommittableAmount =
            LibUint256.min((currentMaxDailyCommittableAmount * _period) / 1 days, currentBalanceToDeposit);
        // we only commit multiples of 32 ETH
        currentMaxCommittableAmount = (currentMaxCommittableAmount / DEPOSIT_SIZE) * DEPOSIT_SIZE;

        if (currentMaxCommittableAmount > 0) {
            _setCommittedBalance(CommittedBalance.get() + currentMaxCommittableAmount);
            _setBalanceToDeposit(currentBalanceToDeposit - currentMaxCommittableAmount);
        }
    }
}

File 12 of 116 : 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 13 of 116 : IOperatorRegistry.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../state/operatorsRegistry/Operators.2.sol";

/// @title Operators Registry Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the list of operators and their keys
interface IOperatorsRegistryV1 {
    /// @notice A new operator has been added to the registry
    /// @param index The operator index
    /// @param name The operator display name
    /// @param operatorAddress The operator address
    event AddedOperator(uint256 indexed index, string name, address indexed operatorAddress);

    /// @notice The operator status has been changed
    /// @param index The operator index
    /// @param active True if the operator is active
    event SetOperatorStatus(uint256 indexed index, bool active);

    /// @notice The operator limit has been changed
    /// @param index The operator index
    /// @param newLimit The new operator staking limit
    event SetOperatorLimit(uint256 indexed index, uint256 newLimit);

    /// @notice The operator stopped validator count has been changed
    /// @param index The operator index
    /// @param newStoppedValidatorCount The new stopped validator count
    event SetOperatorStoppedValidatorCount(uint256 indexed index, uint256 newStoppedValidatorCount);

    /// @notice The operator address has been changed
    /// @param index The operator index
    /// @param newOperatorAddress The new operator address
    event SetOperatorAddress(uint256 indexed index, address indexed newOperatorAddress);

    /// @notice The operator display name has been changed
    /// @param index The operator index
    /// @param newName The new display name
    event SetOperatorName(uint256 indexed index, string newName);

    /// @notice The operator or the admin added new validator keys and signatures
    /// @dev The public keys and signatures are concatenated
    /// @dev A public key is 48 bytes long
    /// @dev A signature is 96 bytes long
    /// @dev [P1, S1, P2, S2, ..., PN, SN] where N is the bytes length divided by (96 + 48)
    /// @param index The operator index
    /// @param publicKeysAndSignatures The concatenated public keys and signatures
    event AddedValidatorKeys(uint256 indexed index, bytes publicKeysAndSignatures);

    /// @notice The operator or the admin removed a public key and its signature from the registry
    /// @param index The operator index
    /// @param publicKey The BLS public key that has been removed
    event RemovedValidatorKey(uint256 indexed index, bytes publicKey);

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

    /// @notice The operator edited its keys after the snapshot block
    /// @dev This means that we cannot assume that its key set is checked by the snapshot
    /// @dev This happens only if the limit was meant to be increased
    /// @param index The operator index
    /// @param currentLimit The current operator limit
    /// @param newLimit The new operator limit that was attempted to be set
    /// @param latestKeysEditBlockNumber The last block number at which the operator changed its keys
    /// @param snapshotBlock The block number of the snapshot
    event OperatorEditsAfterSnapshot(
        uint256 indexed index,
        uint256 currentLimit,
        uint256 newLimit,
        uint256 indexed latestKeysEditBlockNumber,
        uint256 indexed snapshotBlock
    );

    /// @notice The call didn't alter the limit of the operator
    /// @param index The operator index
    /// @param limit The limit of the operator
    event OperatorLimitUnchanged(uint256 indexed index, uint256 limit);

    /// @notice The stopped validator array has been changed
    /// @notice A validator is considered stopped if exiting, exited or slashed
    /// @notice This event is emitted when the oracle reports new stopped validators counts
    /// @param stoppedValidatorCounts The new stopped validator counts
    event UpdatedStoppedValidators(uint32[] stoppedValidatorCounts);

    /// @notice The requested exit count has been updated
    /// @param index The operator index
    /// @param count The count of requested exits
    event RequestedValidatorExits(uint256 indexed index, uint256 count);

    /// @notice The exit request demand has been updated
    /// @param previousValidatorExitsDemand The previous exit request demand
    /// @param nextValidatorExitsDemand The new exit request demand
    event SetCurrentValidatorExitsDemand(uint256 previousValidatorExitsDemand, uint256 nextValidatorExitsDemand);

    /// @notice The total requested exit has been updated
    /// @param previousTotalValidatorExitsRequested The previous total requested exit
    /// @param newTotalValidatorExitsRequested The new total requested exit
    event SetTotalValidatorExitsRequested(
        uint256 previousTotalValidatorExitsRequested, uint256 newTotalValidatorExitsRequested
    );

    /// @notice A validator key got funded on the deposit contract
    /// @notice This event was introduced during a contract upgrade, in order to cover all possible public keys, this event
    /// @notice will be replayed for past funded keys in order to have a complete coverage of all the funded public keys.
    /// @notice In this particuliar scenario, the deferred value will be set to true, to indicate that we are not going to have
    /// @notice the expected additional events and side effects in the same transaction (deposit to official DepositContract etc ...) because
    /// @notice the event was synthetically crafted.
    /// @param index The operator index
    /// @param publicKeys BLS Public key that got funded
    /// @param deferred True if event has been replayed in the context of a migration
    event FundedValidatorKeys(uint256 indexed index, bytes[] publicKeys, bool deferred);

    /// @notice The requested exit count has been update to fill the gap with the reported stopped count
    /// @param index The operator index
    /// @param oldRequestedExits The old requested exit count
    /// @param newRequestedExits The new requested exit count
    event UpdatedRequestedValidatorExitsUponStopped(
        uint256 indexed index, uint32 oldRequestedExits, uint32 newRequestedExits
    );

    /// @notice The calling operator is inactive
    /// @param index The operator index
    error InactiveOperator(uint256 index);

    /// @notice A funded key deletion has been attempted
    error InvalidFundedKeyDeletionAttempt();

    /// @notice The index provided are not sorted properly (descending order)
    error InvalidUnsortedIndexes();

    /// @notice The provided operator and limits array have different lengths
    error InvalidArrayLengths();

    /// @notice The provided operator and limits array are empty
    error InvalidEmptyArray();

    /// @notice The provided key count is 0
    error InvalidKeyCount();

    /// @notice The provided concatenated keys do not have the expected length
    error InvalidKeysLength();

    /// @notice The index that is removed is out of bounds
    error InvalidIndexOutOfBounds();

    /// @notice The value for the operator limit is too high
    /// @param index The operator index
    /// @param limit The new limit provided
    /// @param keyCount The operator key count
    error OperatorLimitTooHigh(uint256 index, uint256 limit, uint256 keyCount);

    /// @notice The value for the limit is too low
    /// @param index The operator index
    /// @param limit The new limit provided
    /// @param fundedKeyCount The operator funded key count
    error OperatorLimitTooLow(uint256 index, uint256 limit, uint256 fundedKeyCount);

    /// @notice The provided list of operators is not in increasing order
    error UnorderedOperatorList();

    /// @notice Thrown when an invalid empty stopped validator array is provided
    error InvalidEmptyStoppedValidatorCountsArray();

    /// @notice Thrown when the sum of stopped validators is invalid
    error InvalidStoppedValidatorCountsSum();

    /// @notice Throw when an element in the stopped validator array is decreasing
    error StoppedValidatorCountsDecreased();

    /// @notice Thrown when the number of elements in the array is too high compared to operator count
    error StoppedValidatorCountsTooHigh();

    /// @notice Thrown when no exit requests can be performed
    error NoExitRequestsToPerform();

    /// @notice The provided stopped validator count array is shrinking
    error StoppedValidatorCountArrayShrinking();

    /// @notice The provided stopped validator count of an operator is above its funded validator count
    error StoppedValidatorCountAboveFundedCount(uint256 operatorIndex, uint32 stoppedCount, uint32 fundedCount);

    /// @notice Initializes the operators registry
    /// @param _admin Admin in charge of managing operators
    /// @param _river Address of River system
    function initOperatorsRegistryV1(address _admin, address _river) external;

    /// @notice Initializes the operators registry for V1_1
    function initOperatorsRegistryV1_1() external;

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

    /// @notice Get operator details
    /// @param _index The index of the operator
    /// @return The details of the operator
    function getOperator(uint256 _index) external view returns (OperatorsV2.Operator memory);

    /// @notice Get operator count
    /// @return The operator count
    function getOperatorCount() external view returns (uint256);

    /// @notice Retrieve the stopped validator count for an operator index
    /// @param _idx The index of the operator
    /// @return The stopped validator count of the operator
    function getOperatorStoppedValidatorCount(uint256 _idx) external view returns (uint32);

    /// @notice Retrieve the total stopped validator count
    /// @return The total stopped validator count
    function getTotalStoppedValidatorCount() external view returns (uint32);

    /// @notice Retrieve the total requested exit count
    /// @notice This value is the amount of exit requests that have been performed, emitting an event for operators to catch
    /// @return The total requested exit count
    function getTotalValidatorExitsRequested() external view returns (uint256);

    /// @notice Get the current exit request demand waiting to be triggered
    /// @notice This value is the amount of exit requests that are demanded and not yet performed by the contract
    /// @return The current exit request demand
    function getCurrentValidatorExitsDemand() external view returns (uint256);

    /// @notice Retrieve the total stopped and requested exit count
    /// @return The total stopped count
    /// @return The total requested exit count
    function getStoppedAndRequestedExitCounts() external view returns (uint32, uint256);

    /// @notice Retrieve the raw stopped validators array from storage
    /// @return The stopped validator array
    function getStoppedValidatorCountPerOperator() external view returns (uint32[] memory);

    /// @notice Get the details of a validator
    /// @param _operatorIndex The index of the operator
    /// @param _validatorIndex The index of the validator
    /// @return publicKey The public key of the validator
    /// @return signature The signature used during deposit
    /// @return funded True if validator has been funded
    function getValidator(uint256 _operatorIndex, uint256 _validatorIndex)
        external
        view
        returns (bytes memory publicKey, bytes memory signature, bool funded);

    /// @notice Retrieve the active operator set
    /// @return The list of active operators and their details
    function listActiveOperators() external view returns (OperatorsV2.Operator[] memory);

    /// @notice Allows river to override the stopped validators array
    /// @notice This actions happens during the Oracle report processing
    /// @param _stoppedValidatorCounts The new stopped validators array
    /// @param _depositedValidatorCount The total deposited validator count
    function reportStoppedValidatorCounts(uint32[] calldata _stoppedValidatorCounts, uint256 _depositedValidatorCount)
        external;

    /// @notice Adds an operator to the registry
    /// @dev Only callable by the administrator
    /// @param _name The name identifying the operator
    /// @param _operator The address representing the operator, receiving the rewards
    /// @return The index of the new operator
    function addOperator(string calldata _name, address _operator) external returns (uint256);

    /// @notice Changes the operator address of an operator
    /// @dev Only callable by the administrator or the previous operator address
    /// @param _index The operator index
    /// @param _newOperatorAddress The new address of the operator
    function setOperatorAddress(uint256 _index, address _newOperatorAddress) external;

    /// @notice Changes the operator name
    /// @dev Only callable by the administrator or the operator
    /// @param _index The operator index
    /// @param _newName The new operator name
    function setOperatorName(uint256 _index, string calldata _newName) external;

    /// @notice Changes the operator status
    /// @dev Only callable by the administrator
    /// @param _index The operator index
    /// @param _newStatus The new status of the operator
    function setOperatorStatus(uint256 _index, bool _newStatus) external;

    /// @notice Changes the operator staking limit
    /// @dev Only callable by the administrator
    /// @dev The operator indexes must be in increasing order and contain no duplicate
    /// @dev The limit cannot exceed the total key count of the operator
    /// @dev The _indexes and _newLimits must have the same length.
    /// @dev Each limit value is applied to the operator index at the same index in the _indexes array.
    /// @param _operatorIndexes The operator indexes, in increasing order and duplicate free
    /// @param _newLimits The new staking limit of the operators
    /// @param _snapshotBlock The block number at which the snapshot was computed
    function setOperatorLimits(
        uint256[] calldata _operatorIndexes,
        uint32[] calldata _newLimits,
        uint256 _snapshotBlock
    ) external;

    /// @notice Adds new keys for an operator
    /// @dev Only callable by the administrator or the operator address
    /// @param _index The operator index
    /// @param _keyCount The amount of keys provided
    /// @param _publicKeysAndSignatures Public keys of the validator, concatenated
    function addValidators(uint256 _index, uint32 _keyCount, bytes calldata _publicKeysAndSignatures) external;

    /// @notice Remove validator keys
    /// @dev Only callable by the administrator or the operator address
    /// @dev The indexes must be provided sorted in decreasing order and duplicate-free, otherwise the method will revert
    /// @dev The operator limit will be set to the lowest deleted key index if the operator's limit wasn't equal to its total key count
    /// @dev The operator or the admin cannot remove funded keys
    /// @dev When removing validators, the indexes of specific unfunded keys can be changed in order to properly
    /// @dev remove the keys from the storage array. Beware of this specific behavior when chaining calls as the
    /// @dev targeted public key indexes can point to a different key after a first call was made and performed
    /// @dev some swaps
    /// @param _index The operator index
    /// @param _indexes The indexes of the keys to remove
    function removeValidators(uint256 _index, uint256[] calldata _indexes) external;

    /// @notice Retrieve validator keys based on operator statuses
    /// @param _count Max amount of keys requested
    /// @return publicKeys An array of public keys
    /// @return signatures An array of signatures linked to the public keys
    function pickNextValidatorsToDeposit(uint256 _count)
        external
        returns (bytes[] memory publicKeys, bytes[] memory signatures);

    /// @notice Public endpoint to consume the exit request demand and perform the actual exit requests
    /// @notice The selection algorithm will pick validators based on their active validator counts
    /// @notice This value is computed by using the count of funded keys and taking into account the stopped validator counts and exit requests
    /// @param _count Max amount of exits to request
    function requestValidatorExits(uint256 _count) external;

    /// @notice Increases the exit request demand
    /// @dev This method is only callable by the river contract, and to actually forward the information to the node operators via event emission, the unprotected requestValidatorExits method must be called
    /// @param _count The amount of exit requests to add to the demand
    /// @param _depositedValidatorCount The total deposited validator count
    function demandValidatorExits(uint256 _count, uint256 _depositedValidatorCount) external;
}

File 14 of 116 : 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 15 of 116 : IWithdraw.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Withdraw Interface (V1)
/// @author Kiln
/// @notice This contract is in charge of holding the exit and skimming funds and allow river to pull these funds
interface IWithdrawV1 {
    /// @notice Emitted when the linked River address is changed
    /// @param river The new River address
    event SetRiver(address river);

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

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

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

    /// @notice Callable by River, sends the specified amount of ETH to River
    /// @param _amount The amount to pull
    function pullEth(uint256 _amount) external;
}

File 16 of 116 : IELFeeRecipient.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Execution Layer Fee Recipient Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to receive all the execution layer fees from the proposed blocks + bribes
interface IELFeeRecipientV1 {
    /// @notice The storage river address has changed
    /// @param river The new river address
    event SetRiver(address indexed river);

    /// @notice The fallback has been triggered
    error InvalidCall();

    /// @notice Initialize the fee recipient with the required arguments
    /// @param _riverAddress Address of River
    function initELFeeRecipientV1(address _riverAddress) external;

    /// @notice Pulls ETH to the River contract
    /// @dev Only callable by the River contract
    /// @param _maxAmount The maximum amount to pull into the system
    function pullELFees(uint256 _maxAmount) external;

    /// @notice Ether receiver
    receive() external payable;

    /// @notice Invalid fallback detector
    fallback() external payable;
}

File 17 of 116 : ICoverageFund.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Coverage Fund Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to receive donations for the slashing coverage fund and pull the funds into river
interface ICoverageFundV1 {
    /// @notice The storage river address has changed
    /// @param river The new river address
    event SetRiver(address indexed river);

    /// @notice A donation has been made to the coverage fund
    /// @param donator Address that performed the donation
    /// @param amount The amount donated
    event Donate(address indexed donator, uint256 amount);

    /// @notice The fallback or receive callback has been triggered
    error InvalidCall();

    /// @notice A donation with 0 ETH has been performed
    error EmptyDonation();

    /// @notice Initialize the coverage fund with the required arguments
    /// @param _riverAddress Address of River
    function initCoverageFundV1(address _riverAddress) external;

    /// @notice Pulls ETH into the River contract
    /// @dev Only callable by the River contract
    /// @param _maxAmount The maximum amount to pull into the system
    function pullCoverageFunds(uint256 _maxAmount) external;

    /// @notice Donates ETH to the coverage fund contract
    function donate() external payable;

    /// @notice Ether receiver
    receive() external payable;

    /// @notice Invalid fallback detector
    fallback() external payable;
}

File 18 of 116 : ConsensusLayerDepositManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../interfaces/components/IConsensusLayerDepositManager.1.sol";
import "../interfaces/IDepositContract.sol";

import "../libraries/LibBytes.sol";
import "../libraries/LibUint256.sol";

import "../state/river/DepositContractAddress.sol";
import "../state/river/WithdrawalCredentials.sol";
import "../state/river/DepositedValidatorCount.sol";
import "../state/river/BalanceToDeposit.sol";
import "../state/river/CommittedBalance.sol";

/// @title Consensus Layer Deposit Manager (v1)
/// @author Kiln
/// @notice This contract handles the interactions with the official deposit contract, funding all validators
/// @notice Whenever a deposit to the consensus layer is requested, this contract computed the amount of keys
/// @notice that could be deposited depending on the amount available in the contract. It then tries to retrieve
/// @notice validator keys by calling its internal virtual method _getNextValidators. This method should be
/// @notice overridden by the implementing contract to provide [0; _keyCount] keys when invoked.
abstract contract ConsensusLayerDepositManagerV1 is IConsensusLayerDepositManagerV1 {
    /// @notice Size of a BLS Public key in bytes
    uint256 public constant PUBLIC_KEY_LENGTH = 48;
    /// @notice Size of a BLS Signature in bytes
    uint256 public constant SIGNATURE_LENGTH = 96;
    /// @notice Size of a deposit in ETH
    uint256 public constant DEPOSIT_SIZE = 32 ether;

    /// @notice Handler called to retrieve the internal River admin address
    /// @dev Must be Overridden
    function _getRiverAdmin() internal view virtual returns (address);

    /// @notice Handler called to change the committed balance to deposit
    /// @param newCommittedBalance The new committed balance value
    function _setCommittedBalance(uint256 newCommittedBalance) internal virtual;

    /// @notice Internal helper to retrieve validator keys ready to be funded
    /// @dev Must be overridden
    /// @param _keyCount The amount of keys (or less) to return.
    function _getNextValidators(uint256 _keyCount)
        internal
        virtual
        returns (bytes[] memory publicKeys, bytes[] memory signatures);

    /// @notice Initializer to set the deposit contract address and the withdrawal credentials to use
    /// @param _depositContractAddress The address of the deposit contract
    /// @param _withdrawalCredentials The withdrawal credentials to apply to all deposits
    function initConsensusLayerDepositManagerV1(address _depositContractAddress, bytes32 _withdrawalCredentials)
        internal
    {
        DepositContractAddress.set(_depositContractAddress);
        emit SetDepositContractAddress(_depositContractAddress);

        WithdrawalCredentials.set(_withdrawalCredentials);
        emit SetWithdrawalCredentials(_withdrawalCredentials);
    }

    /// @inheritdoc IConsensusLayerDepositManagerV1
    function getCommittedBalance() external view returns (uint256) {
        return CommittedBalance.get();
    }

    /// @inheritdoc IConsensusLayerDepositManagerV1
    function getBalanceToDeposit() external view returns (uint256) {
        return BalanceToDeposit.get();
    }

    /// @inheritdoc IConsensusLayerDepositManagerV1
    function getWithdrawalCredentials() external view returns (bytes32) {
        return WithdrawalCredentials.get();
    }

    /// @inheritdoc IConsensusLayerDepositManagerV1
    function getDepositedValidatorCount() external view returns (uint256) {
        return DepositedValidatorCount.get();
    }

    /// @inheritdoc IConsensusLayerDepositManagerV1
    function depositToConsensusLayer(uint256 _maxCount) external {
        uint256 committedBalance = CommittedBalance.get();
        uint256 keyToDepositCount = LibUint256.min(committedBalance / DEPOSIT_SIZE, _maxCount);

        if (keyToDepositCount == 0) {
            revert NotEnoughFunds();
        }

        // it's up to the internal overriden _getNextValidators method to provide two array of the same
        // size for the publicKeys and the signatures
        (bytes[] memory publicKeys, bytes[] memory signatures) = _getNextValidators(keyToDepositCount);

        uint256 receivedPublicKeyCount = publicKeys.length;

        if (receivedPublicKeyCount == 0) {
            revert NoAvailableValidatorKeys();
        }

        if (receivedPublicKeyCount > keyToDepositCount) {
            revert InvalidPublicKeyCount();
        }

        bytes32 withdrawalCredentials = WithdrawalCredentials.get();

        if (withdrawalCredentials == 0) {
            revert InvalidWithdrawalCredentials();
        }

        for (uint256 idx = 0; idx < receivedPublicKeyCount;) {
            _depositValidator(publicKeys[idx], signatures[idx], withdrawalCredentials);
            unchecked {
                ++idx;
            }
        }
        _setCommittedBalance(committedBalance - DEPOSIT_SIZE * receivedPublicKeyCount);
        uint256 currentDepositedValidatorCount = DepositedValidatorCount.get();
        DepositedValidatorCount.set(currentDepositedValidatorCount + receivedPublicKeyCount);
        emit SetDepositedValidatorCount(
            currentDepositedValidatorCount, currentDepositedValidatorCount + receivedPublicKeyCount
        );
    }

    /// @notice Deposits 32 ETH to the official Deposit contract
    /// @param _publicKey The public key of the validator
    /// @param _signature The signature provided by the operator
    /// @param _withdrawalCredentials The withdrawal credentials provided by River
    function _depositValidator(bytes memory _publicKey, bytes memory _signature, bytes32 _withdrawalCredentials)
        internal
    {
        if (_publicKey.length != PUBLIC_KEY_LENGTH) {
            revert InconsistentPublicKeys();
        }

        if (_signature.length != SIGNATURE_LENGTH) {
            revert InconsistentSignatures();
        }
        uint256 value = DEPOSIT_SIZE;

        uint256 depositAmount = value / 1 gwei;

        bytes32 pubkeyRoot = sha256(bytes.concat(_publicKey, bytes16(0)));
        bytes32 signatureRoot = sha256(
            bytes.concat(
                sha256(LibBytes.slice(_signature, 0, 64)),
                sha256(bytes.concat(LibBytes.slice(_signature, 64, SIGNATURE_LENGTH - 64), bytes32(0)))
            )
        );

        bytes32 depositDataRoot = sha256(
            bytes.concat(
                sha256(bytes.concat(pubkeyRoot, _withdrawalCredentials)),
                sha256(bytes.concat(bytes32(LibUint256.toLittleEndian64(depositAmount)), signatureRoot))
            )
        );

        uint256 targetBalance = address(this).balance - value;

        IDepositContract(DepositContractAddress.get()).deposit{value: value}(
            _publicKey, abi.encodePacked(_withdrawalCredentials), _signature, depositDataRoot
        );
        if (address(this).balance != targetBalance) {
            revert ErrorOnDeposit();
        }
    }
}

File 19 of 116 : UserDepositManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../interfaces/components/IUserDepositManager.1.sol";

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

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

/// @title User Deposit Manager (v1)
/// @author Kiln
/// @notice This contract handles the inbound transfers cases or the explicit submissions
abstract contract UserDepositManagerV1 is IUserDepositManagerV1 {
    /// @notice Handler called whenever a user has sent funds to the contract
    /// @dev Must be overridden
    /// @param _depositor Address that made the deposit
    /// @param _recipient Address that receives the minted shares
    /// @param _amount Amount deposited
    function _onDeposit(address _depositor, address _recipient, uint256 _amount) internal virtual;
    function _setBalanceToDeposit(uint256 newBalanceToDeposit) internal virtual;

    /// @inheritdoc IUserDepositManagerV1
    function deposit() external payable {
        _deposit(msg.sender);
    }

    /// @inheritdoc IUserDepositManagerV1
    function depositAndTransfer(address _recipient) external payable {
        LibSanitize._notZeroAddress(_recipient);
        _deposit(_recipient);
    }

    /// @inheritdoc IUserDepositManagerV1
    receive() external payable {
        _deposit(msg.sender);
    }

    /// @inheritdoc IUserDepositManagerV1
    fallback() external payable {
        revert LibErrors.InvalidCall();
    }

    /// @notice Internal utility calling the deposit handler and emitting the deposit details
    /// @param _recipient The account receiving the minted shares
    function _deposit(address _recipient) internal {
        if (msg.value == 0) {
            revert EmptyDeposit();
        }

        _setBalanceToDeposit(BalanceToDeposit.get() + msg.value);

        _onDeposit(msg.sender, _recipient, msg.value);

        emit UserDeposit(msg.sender, _recipient, msg.value);
    }
}

File 20 of 116 : SharesManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../interfaces/components/ISharesManager.1.sol";

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

import "../state/river/Shares.sol";
import "../state/river/SharesPerOwner.sol";
import "../state/shared/ApprovalsPerOwner.sol";

/// @title Shares Manager (v1)
/// @author Kiln
/// @notice This contract handles the shares of the depositor and the ERC20 interface
abstract contract SharesManagerV1 is ISharesManagerV1 {
    /// @notice Internal hook triggered on the external transfer call
    /// @dev Must be overridden
    /// @param _from Address of the sender
    /// @param _to Address of the recipient
    function _onTransfer(address _from, address _to) internal view virtual;

    /// @notice Internal method to override to provide the total underlying asset balance
    /// @dev Must be overridden
    /// @return The total asset balance of the system
    function _assetBalance() internal view virtual returns (uint256);

    /// @notice Modifier used to ensure that the transfer is allowed by using the internal hook to perform internal checks
    /// @param _from Address of the sender
    /// @param _to Address of the recipient
    modifier transferAllowed(address _from, address _to) {
        _onTransfer(_from, _to);
        _;
    }

    /// @notice Modifier used to ensure the amount transferred is not 0
    /// @param _value Amount to check
    modifier isNotZero(uint256 _value) {
        if (_value == 0) {
            revert NullTransfer();
        }
        _;
    }

    /// @notice Modifier used to ensure that the sender has enough funds for the transfer
    /// @param _owner Address of the sender
    /// @param _value Value that is required to be sent
    modifier hasFunds(address _owner, uint256 _value) {
        if (_balanceOf(_owner) < _value) {
            revert BalanceTooLow();
        }
        _;
    }

    /// @inheritdoc ISharesManagerV1
    function name() external pure returns (string memory) {
        return "Liquid Staked ETH";
    }

    /// @inheritdoc ISharesManagerV1
    function symbol() external pure returns (string memory) {
        return "LsETH";
    }

    /// @inheritdoc ISharesManagerV1
    function decimals() external pure returns (uint8) {
        return 18;
    }

    /// @inheritdoc ISharesManagerV1
    function totalSupply() external view returns (uint256) {
        return _totalSupply();
    }

    /// @inheritdoc ISharesManagerV1
    function totalUnderlyingSupply() external view returns (uint256) {
        return _assetBalance();
    }

    /// @inheritdoc ISharesManagerV1
    function balanceOf(address _owner) external view returns (uint256) {
        return _balanceOf(_owner);
    }

    /// @inheritdoc ISharesManagerV1
    function balanceOfUnderlying(address _owner) public view returns (uint256) {
        return _balanceFromShares(SharesPerOwner.get(_owner));
    }

    /// @inheritdoc ISharesManagerV1
    function underlyingBalanceFromShares(uint256 _shares) external view returns (uint256) {
        return _balanceFromShares(_shares);
    }

    /// @inheritdoc ISharesManagerV1
    function sharesFromUnderlyingBalance(uint256 _underlyingAssetAmount) external view returns (uint256) {
        return _sharesFromBalance(_underlyingAssetAmount);
    }

    /// @inheritdoc ISharesManagerV1
    function allowance(address _owner, address _spender) external view returns (uint256) {
        return ApprovalsPerOwner.get(_owner, _spender);
    }

    /// @inheritdoc ISharesManagerV1
    function transfer(address _to, uint256 _value)
        external
        transferAllowed(msg.sender, _to)
        isNotZero(_value)
        hasFunds(msg.sender, _value)
        returns (bool)
    {
        if (_to == address(0)) {
            revert UnauthorizedTransfer(msg.sender, address(0));
        }
        return _transfer(msg.sender, _to, _value);
    }

    /// @inheritdoc ISharesManagerV1
    function transferFrom(address _from, address _to, uint256 _value)
        external
        transferAllowed(_from, _to)
        isNotZero(_value)
        hasFunds(_from, _value)
        returns (bool)
    {
        if (_to == address(0)) {
            revert UnauthorizedTransfer(_from, address(0));
        }
        _spendAllowance(_from, _value);
        return _transfer(_from, _to, _value);
    }

    /// @inheritdoc ISharesManagerV1
    function approve(address _spender, uint256 _value) external returns (bool) {
        _approve(msg.sender, _spender, _value);
        return true;
    }

    /// @inheritdoc ISharesManagerV1
    function increaseAllowance(address _spender, uint256 _additionalValue) external returns (bool) {
        _approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) + _additionalValue);
        return true;
    }

    /// @inheritdoc ISharesManagerV1
    function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool) {
        _approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) - _subtractableValue);
        return true;
    }

    /// @notice Internal utility to spend the allowance of an account from the message sender
    /// @param _from Address owning the allowance
    /// @param _value Amount of allowance in shares to spend
    function _spendAllowance(address _from, uint256 _value) internal {
        uint256 currentAllowance = ApprovalsPerOwner.get(_from, msg.sender);
        if (currentAllowance < _value) {
            revert AllowanceTooLow(_from, msg.sender, currentAllowance, _value);
        }
        if (currentAllowance != type(uint256).max) {
            _approve(_from, msg.sender, currentAllowance - _value);
        }
    }

    /// @notice Internal utility to change the allowance of an owner to a spender
    /// @param _owner The owner of the shares
    /// @param _spender The allowed spender of the shares
    /// @param _value The new allowance value
    function _approve(address _owner, address _spender, uint256 _value) internal {
        LibSanitize._notZeroAddress(_owner);
        LibSanitize._notZeroAddress(_spender);
        ApprovalsPerOwner.set(_owner, _spender, _value);
        emit Approval(_owner, _spender, _value);
    }

    /// @notice Internal utility to retrieve the total supply of tokens
    /// @return The total supply
    function _totalSupply() internal view returns (uint256) {
        return Shares.get();
    }

    /// @notice Internal utility to perform an unchecked transfer
    /// @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 _transfer(address _from, address _to, uint256 _value) internal returns (bool) {
        SharesPerOwner.set(_from, SharesPerOwner.get(_from) - _value);
        SharesPerOwner.set(_to, SharesPerOwner.get(_to) + _value);

        emit Transfer(_from, _to, _value);

        return true;
    }

    /// @notice Internal utility to retrieve the underlying asset balance for the given shares
    /// @param _shares Amount of shares to convert
    /// @return The balance from the given shares
    function _balanceFromShares(uint256 _shares) internal view returns (uint256) {
        uint256 _totalSharesValue = Shares.get();

        if (_totalSharesValue == 0) {
            return 0;
        }

        return ((_shares * _assetBalance())) / _totalSharesValue;
    }

    /// @notice Internal utility to retrieve the shares count for a given underlying asset amount
    /// @param _balance Amount of underlying asset balance to convert
    /// @return The shares from the given balance
    function _sharesFromBalance(uint256 _balance) internal view returns (uint256) {
        uint256 _totalSharesValue = Shares.get();

        if (_totalSharesValue == 0) {
            return 0;
        }

        return (_balance * _totalSharesValue) / _assetBalance();
    }

    /// @notice Internal utility to mint shares for the specified user
    /// @dev This method assumes that funds received are now part of the _assetBalance()
    /// @param _owner Account that should receive the new shares
    /// @param _underlyingAssetValue Value of underlying asset received, to convert into shares
    /// @return sharesToMint The amnount of minted shares
    function _mintShares(address _owner, uint256 _underlyingAssetValue) internal returns (uint256 sharesToMint) {
        uint256 oldTotalAssetBalance = _assetBalance() - _underlyingAssetValue;

        if (oldTotalAssetBalance == 0) {
            sharesToMint = _underlyingAssetValue;
            _mintRawShares(_owner, _underlyingAssetValue);
        } else {
            sharesToMint = (_underlyingAssetValue * _totalSupply()) / oldTotalAssetBalance;
            _mintRawShares(_owner, sharesToMint);
        }
    }

    /// @notice Internal utility to retrieve the amount of shares per owner
    /// @param _owner Account to be checked
    /// @return The balance of the account in shares
    function _balanceOf(address _owner) internal view returns (uint256) {
        return SharesPerOwner.get(_owner);
    }

    /// @notice Internal utility to mint shares without any conversion, and emits a mint Transfer event
    /// @param _owner Account that should receive the new shares
    /// @param _value Amount of shares to mint
    function _mintRawShares(address _owner, uint256 _value) internal {
        _setTotalSupply(Shares.get() + _value);
        SharesPerOwner.set(_owner, SharesPerOwner.get(_owner) + _value);
        emit Transfer(address(0), _owner, _value);
    }

    /// @notice Internal utility to burn shares without any conversion, and emits a burn Transfer event
    /// @param _owner Account that should burn its shares
    /// @param _value Amount of shares to burn
    function _burnRawShares(address _owner, uint256 _value) internal {
        _setTotalSupply(Shares.get() - _value);
        SharesPerOwner.set(_owner, SharesPerOwner.get(_owner) - _value);
        emit Transfer(_owner, address(0), _value);
    }

    /// @notice Internal utility to set the total supply and emit an event
    /// @param newTotalSupply The new total supply value
    function _setTotalSupply(uint256 newTotalSupply) internal {
        Shares.set(newTotalSupply);
        emit SetTotalSupply(newTotalSupply);
    }
}

File 21 of 116 : OracleManager.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../interfaces/components/IOracleManager.1.sol";
import "../interfaces/IRedeemManager.1.sol";

import "../libraries/LibUint256.sol";

import "../state/river/LastConsensusLayerReport.sol";
import "../state/river/OracleAddress.sol";
import "../state/river/CLValidatorTotalBalance.sol";
import "../state/river/CLValidatorCount.sol";
import "../state/river/DepositedValidatorCount.sol";
import "../state/river/LastOracleRoundId.sol";

/// @title Oracle Manager (v1)
/// @author Kiln
/// @notice This contract handles the inputs provided by the oracle
/// @notice The Oracle contract is plugged to this contract and is in charge of pushing
/// @notice data whenever a new report has been deemed valid. The report consists in two
/// @notice values: the sum of all balances of all deposited validators and the count of
/// @notice validators that have been activated on the consensus layer.
abstract contract OracleManagerV1 is IOracleManagerV1 {
    uint256 internal constant ONE_YEAR = 365 days;
    /// @notice Size of a deposit in ETH
    uint256 public constant _DEPOSIT_SIZE = 32 ether;

    /// @notice Handler called if the delta between the last and new validator balance sum is positive
    /// @dev Must be overridden
    /// @param _profits The positive increase in the validator balance sum (staking rewards)
    function _onEarnings(uint256 _profits) internal virtual;

    /// @notice Handler called to pull the Execution layer fees from the recipient
    /// @dev Must be overridden
    /// @param _max The maximum amount to pull inside the system
    /// @return The amount pulled inside the system
    function _pullELFees(uint256 _max) internal virtual returns (uint256);

    /// @notice Handler called to pull the coverage funds
    /// @dev Must be overridden
    /// @param _max The maximum amount to pull inside the system
    /// @return The amount pulled inside the system
    function _pullCoverageFunds(uint256 _max) internal virtual returns (uint256);

    /// @notice Handler called to retrieve the system administrator address
    /// @dev Must be overridden
    /// @return The system administrator address
    function _getRiverAdmin() internal view virtual returns (address);

    /// @notice Overridden handler called whenever the total balance of ETH is requested
    /// @return The current total asset balance managed by River
    function _assetBalance() internal view virtual returns (uint256);

    /// @notice Pulls funds from the Withdraw contract, and adds funds to deposit and redeem balances
    /// @param _skimmedEthAmount The new amount of skimmed eth to pull
    /// @param _exitedEthAmount The new amount of exited eth to pull
    function _pullCLFunds(uint256 _skimmedEthAmount, uint256 _exitedEthAmount) internal virtual;

    /// @notice Pulls funds from the redeem manager exceeding eth buffer
    /// @param _max The maximum amount to pull
    /// @return The amount pulled
    function _pullRedeemManagerExceedingEth(uint256 _max) internal virtual returns (uint256);

    /// @notice Use the balance to redeem to report a withdrawal event on the redeem manager
    function _reportWithdrawToRedeemManager() internal virtual;

    /// @notice Requests exits of validators after possibly rebalancing deposit and redeem balances
    /// @param _exitingBalance The currently exiting funds, soon to be received on the execution layer
    /// @param _depositToRedeemRebalancingAllowed True if rebalancing from deposit to redeem is allowed
    function _requestExitsBasedOnRedeemDemandAfterRebalancings(
        uint256 _exitingBalance,
        uint32[] memory _stoppedValidatorCounts,
        bool _depositToRedeemRebalancingAllowed,
        bool _slashingContainmentModeEnabled
    ) internal virtual;

    /// @notice Skims the redeem balance and sends remaining funds to the deposit balance
    function _skimExcessBalanceToRedeem() internal virtual;

    /// @notice Commits the deposit balance up to the allowed daily limit
    /// @param _period The period between current and last report
    function _commitBalanceToDeposit(uint256 _period) internal virtual;

    /// @notice Prevents unauthorized calls
    modifier onlyAdmin_OMV1() {
        if (msg.sender != _getRiverAdmin()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @notice Set the initial oracle address
    /// @param _oracle Address of the oracle
    function initOracleManagerV1(address _oracle) internal {
        OracleAddress.set(_oracle);
        emit SetOracle(_oracle);
    }

    /// @notice Initializes version 1.1 of the oracle manager
    /// @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
    function initOracleManagerV1_1(
        uint64 _epochsPerFrame,
        uint64 _slotsPerEpoch,
        uint64 _secondsPerSlot,
        uint64 _genesisTime,
        uint64 _epochsToAssumedFinality,
        uint256 _annualAprUpperBound,
        uint256 _relativeLowerBound
    ) internal {
        CLSpec.set(
            CLSpec.CLSpecStruct({
                epochsPerFrame: _epochsPerFrame,
                slotsPerEpoch: _slotsPerEpoch,
                secondsPerSlot: _secondsPerSlot,
                genesisTime: _genesisTime,
                epochsToAssumedFinality: _epochsToAssumedFinality
            })
        );
        emit SetSpec(_epochsPerFrame, _slotsPerEpoch, _secondsPerSlot, _genesisTime, _epochsToAssumedFinality);
        ReportBounds.set(
            ReportBounds.ReportBoundsStruct({
                annualAprUpperBound: _annualAprUpperBound,
                relativeLowerBound: _relativeLowerBound
            })
        );
        emit SetBounds(_annualAprUpperBound, _relativeLowerBound);

        IOracleManagerV1.StoredConsensusLayerReport memory storedReport;
        storedReport.epoch = uint256(LastOracleRoundId.get());
        storedReport.validatorsBalance = CLValidatorTotalBalance.get();
        storedReport.validatorsSkimmedBalance = 0;
        storedReport.validatorsExitedBalance = 0;
        storedReport.validatorsExitingBalance = 0;
        storedReport.validatorsCount = uint32(CLValidatorCount.get());
        storedReport.rebalanceDepositToRedeemMode = false;
        storedReport.slashingContainmentMode = false;
        LastConsensusLayerReport.set(storedReport);
    }

    /// @inheritdoc IOracleManagerV1
    function getOracle() external view returns (address) {
        return OracleAddress.get();
    }

    /// @inheritdoc IOracleManagerV1
    function getCLValidatorTotalBalance() external view returns (uint256) {
        return LastConsensusLayerReport.get().validatorsBalance;
    }

    /// @inheritdoc IOracleManagerV1
    function getCLValidatorCount() external view returns (uint256) {
        return LastConsensusLayerReport.get().validatorsCount;
    }

    /// @inheritdoc IOracleManagerV1
    function getExpectedEpochId() external view returns (uint256) {
        CLSpec.CLSpecStruct memory cls = CLSpec.get();
        uint256 currentEpoch = _currentEpoch(cls);
        return LibUint256.max(
            LastConsensusLayerReport.get().epoch + cls.epochsPerFrame,
            currentEpoch - (currentEpoch % cls.epochsPerFrame)
        );
    }

    /// @inheritdoc IOracleManagerV1
    function isValidEpoch(uint256 _epoch) external view returns (bool) {
        return _isValidEpoch(CLSpec.get(), _epoch);
    }

    /// @inheritdoc IOracleManagerV1
    function getTime() external view returns (uint256) {
        return block.timestamp;
    }

    /// @inheritdoc IOracleManagerV1
    function getLastCompletedEpochId() external view returns (uint256) {
        return LastConsensusLayerReport.get().epoch;
    }

    /// @inheritdoc IOracleManagerV1
    function getCurrentEpochId() external view returns (uint256) {
        return _currentEpoch(CLSpec.get());
    }

    /// @inheritdoc IOracleManagerV1
    function getCLSpec() external view returns (CLSpec.CLSpecStruct memory) {
        return CLSpec.get();
    }

    /// @inheritdoc IOracleManagerV1
    function getCurrentFrame() external view returns (uint256 _startEpochId, uint256 _startTime, uint256 _endTime) {
        CLSpec.CLSpecStruct memory cls = CLSpec.get();
        uint256 currentEpoch = _currentEpoch(cls);
        _startEpochId = currentEpoch - (currentEpoch % cls.epochsPerFrame);
        _startTime = _startEpochId * cls.slotsPerEpoch * cls.secondsPerSlot;
        _endTime = (_startEpochId + cls.epochsPerFrame) * cls.slotsPerEpoch * cls.secondsPerSlot - 1;
    }

    /// @inheritdoc IOracleManagerV1
    function getFrameFirstEpochId(uint256 _epochId) external view returns (uint256) {
        return _epochId - (_epochId % CLSpec.get().epochsPerFrame);
    }

    /// @inheritdoc IOracleManagerV1
    function getReportBounds() external view returns (ReportBounds.ReportBoundsStruct memory) {
        return ReportBounds.get();
    }

    /// @inheritdoc IOracleManagerV1
    function getLastConsensusLayerReport() external view returns (IOracleManagerV1.StoredConsensusLayerReport memory) {
        return LastConsensusLayerReport.get();
    }

    /// @inheritdoc IOracleManagerV1
    function setOracle(address _oracleAddress) external onlyAdmin_OMV1 {
        OracleAddress.set(_oracleAddress);
        emit SetOracle(_oracleAddress);
    }

    /// @inheritdoc IOracleManagerV1
    function setCLSpec(CLSpec.CLSpecStruct calldata _newValue) external onlyAdmin_OMV1 {
        CLSpec.set(_newValue);
        emit SetSpec(
            _newValue.epochsPerFrame,
            _newValue.slotsPerEpoch,
            _newValue.secondsPerSlot,
            _newValue.genesisTime,
            _newValue.epochsToAssumedFinality
        );
    }

    /// @inheritdoc IOracleManagerV1
    function setReportBounds(ReportBounds.ReportBoundsStruct calldata _newValue) external onlyAdmin_OMV1 {
        ReportBounds.set(_newValue);
        emit SetBounds(_newValue.annualAprUpperBound, _newValue.relativeLowerBound);
    }

    /// @notice Structure holding internal variables used during reporting
    struct ConsensusLayerDataReportingVariables {
        uint256 preReportUnderlyingBalance;
        uint256 postReportUnderlyingBalance;
        uint256 lastReportExitedBalance;
        uint256 lastReportSkimmedBalance;
        uint256 exitedAmountIncrease;
        uint256 skimmedAmountIncrease;
        uint256 timeElapsedSinceLastReport;
        uint256 availableAmountToUpperBound;
        uint256 redeemManagerDemand;
        ConsensusLayerDataReportingTrace trace;
    }

    /// @inheritdoc IOracleManagerV1
    function setConsensusLayerData(IOracleManagerV1.ConsensusLayerReport calldata _report) external {
        // only the oracle is allowed to call this endpoint
        if (msg.sender != OracleAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        CLSpec.CLSpecStruct memory cls = CLSpec.get();

        // we start by verifying that the reported epoch is valid based on the consensus layer spec
        if (!_isValidEpoch(cls, _report.epoch)) {
            revert InvalidEpoch(_report.epoch);
        }

        ConsensusLayerDataReportingVariables memory vars;

        {
            IOracleManagerV1.StoredConsensusLayerReport storage lastStoredReport = LastConsensusLayerReport.get();

            vars.lastReportExitedBalance = lastStoredReport.validatorsExitedBalance;

            // we ensure that the reported total exited balance is not decreasing
            if (_report.validatorsExitedBalance < vars.lastReportExitedBalance) {
                revert InvalidDecreasingValidatorsExitedBalance(
                    vars.lastReportExitedBalance, _report.validatorsExitedBalance
                );
            }

            // we compute the exited amount increase by taking the delta between reports
            vars.exitedAmountIncrease = _report.validatorsExitedBalance - vars.lastReportExitedBalance;

            vars.lastReportSkimmedBalance = lastStoredReport.validatorsSkimmedBalance;

            // we ensure that the reported total skimmed balance is not decreasing
            if (_report.validatorsSkimmedBalance < vars.lastReportSkimmedBalance) {
                revert InvalidDecreasingValidatorsSkimmedBalance(
                    vars.lastReportSkimmedBalance, _report.validatorsSkimmedBalance
                );
            }

            // we ensure that the reported validator count is not decreasing
            if (
                _report.validatorsCount > DepositedValidatorCount.get()
                    || _report.validatorsCount < lastStoredReport.validatorsCount
            ) {
                revert InvalidValidatorCountReport(
                    _report.validatorsCount, DepositedValidatorCount.get(), lastStoredReport.validatorsCount
                );
            }

            // we compute the new skimmed amount by taking the delta between reports
            vars.skimmedAmountIncrease = _report.validatorsSkimmedBalance - vars.lastReportSkimmedBalance;

            vars.timeElapsedSinceLastReport = _timeBetweenEpochs(cls, lastStoredReport.epoch, _report.epoch);
        }

        // we retrieve the current total underlying balance before any reporting data is applied to the system
        vars.preReportUnderlyingBalance = _assetBalance();

        // if we have new exited / skimmed eth available, we pull funds from the consensus layer recipient
        if (vars.exitedAmountIncrease + vars.skimmedAmountIncrease > 0) {
            // this method pulls and updates ethToDeposit / ethToRedeem accordingly
            _pullCLFunds(vars.skimmedAmountIncrease, vars.exitedAmountIncrease);
        }

        {
            // we update the system parameters, this will have an impact on how the total underlying balance is computed
            IOracleManagerV1.StoredConsensusLayerReport memory storedReport;

            storedReport.epoch = _report.epoch;
            storedReport.validatorsBalance = _report.validatorsBalance;
            storedReport.validatorsSkimmedBalance = _report.validatorsSkimmedBalance;
            storedReport.validatorsExitedBalance = _report.validatorsExitedBalance;
            storedReport.validatorsExitingBalance = _report.validatorsExitingBalance;
            storedReport.validatorsCount = _report.validatorsCount;
            storedReport.rebalanceDepositToRedeemMode = _report.rebalanceDepositToRedeemMode;
            storedReport.slashingContainmentMode = _report.slashingContainmentMode;
            LastConsensusLayerReport.set(storedReport);
        }

        ReportBounds.ReportBoundsStruct memory rb = ReportBounds.get();

        // we compute the maximum allowed increase in balance based on the pre report value
        uint256 maxIncrease = _maxIncrease(rb, vars.preReportUnderlyingBalance, vars.timeElapsedSinceLastReport);

        // we retrieve the new total underlying balance after system parameters are changed
        vars.postReportUnderlyingBalance = _assetBalance();

        // we can now compute the earned rewards from the consensus layer balances
        // in order to properly account for the balance increase, we compare the sums of current balances, skimmed balance and exited balances
        // we also synthetically increase the current balance by 32 eth per new activated validator, this way we have no discrepency due
        // to currently activating funds that were not yet accounted in the consensus layer balances
        if (vars.postReportUnderlyingBalance >= vars.preReportUnderlyingBalance) {
            // if this happens, we revert and the reporting process is cancelled
            if (vars.postReportUnderlyingBalance > vars.preReportUnderlyingBalance + maxIncrease) {
                revert TotalValidatorBalanceIncreaseOutOfBound(
                    vars.preReportUnderlyingBalance,
                    vars.postReportUnderlyingBalance,
                    vars.timeElapsedSinceLastReport,
                    rb.annualAprUpperBound
                );
            }

            // we update the rewards based on the balance delta
            vars.trace.rewards = vars.postReportUnderlyingBalance - vars.preReportUnderlyingBalance;

            // we update the available amount to upper bound (the amount of eth we can still pull and stay below the upper reporting bound)
            vars.availableAmountToUpperBound = maxIncrease - vars.trace.rewards;
        } else {
            // otherwise if the balance has decreased, we verify that we are not exceeding the lower reporting bound

            // we compute the maximum allowed decrease in balance
            uint256 maxDecrease = _maxDecrease(rb, vars.preReportUnderlyingBalance);

            // we verify that the bound is not crossed
            if (
                vars.postReportUnderlyingBalance
                    < vars.preReportUnderlyingBalance - LibUint256.min(maxDecrease, vars.preReportUnderlyingBalance)
            ) {
                revert TotalValidatorBalanceDecreaseOutOfBound(
                    vars.preReportUnderlyingBalance,
                    vars.postReportUnderlyingBalance,
                    vars.timeElapsedSinceLastReport,
                    rb.relativeLowerBound
                );
            }

            // we update the available amount to upper bound to be equal to the maximum allowed increase plus the negative delta due to the loss
            vars.availableAmountToUpperBound =
                maxIncrease + (vars.preReportUnderlyingBalance - vars.postReportUnderlyingBalance);
        }

        // if we have available amount to upper bound after the reporting values are applied
        if (vars.availableAmountToUpperBound > 0) {
            // we pull the funds from the execution layer fee recipient
            vars.trace.pulledELFees = _pullELFees(vars.availableAmountToUpperBound);
            // we update the rewards
            vars.trace.rewards += vars.trace.pulledELFees;
            // we update the available amount accordingly
            vars.availableAmountToUpperBound -= vars.trace.pulledELFees;
        }

        // if we have available amount to upper bound after the execution layer fees are pulled
        if (vars.availableAmountToUpperBound > 0) {
            // we pull the funds from the exceeding eth buffer of the redeem manager
            vars.trace.pulledRedeemManagerExceedingEthBuffer =
                _pullRedeemManagerExceedingEth(vars.availableAmountToUpperBound);
            // we update the available amount accordingly
            vars.availableAmountToUpperBound -= vars.trace.pulledRedeemManagerExceedingEthBuffer;
        }

        // if we have available amount to upper bound after pulling the exceeding eth buffer, we attempt to pull coverage funds
        if (vars.availableAmountToUpperBound > 0) {
            // we pull the funds from the coverage recipient
            vars.trace.pulledCoverageFunds = _pullCoverageFunds(vars.availableAmountToUpperBound);
            // we do not update the rewards as coverage is not considered rewards
            // we do not update the available amount as there are no more pulling actions to perform afterwards
        }

        // if our rewards are not null, we dispatch the fee to the collector
        if (vars.trace.rewards > 0) {
            _onEarnings(vars.trace.rewards);
        }

        _requestExitsBasedOnRedeemDemandAfterRebalancings(
            _report.validatorsExitingBalance,
            _report.stoppedValidatorCountPerOperator,
            _report.rebalanceDepositToRedeemMode,
            _report.slashingContainmentMode
        );

        // we use the updated balanceToRedeem value to report a withdraw event on the redeem manager
        _reportWithdrawToRedeemManager();

        // if funds are left in the balance to redeem, we move them to the deposit balance
        _skimExcessBalanceToRedeem();

        // we update the committable amount based on daily maximum allowed
        _commitBalanceToDeposit(vars.timeElapsedSinceLastReport);

        // we emit a summary event with all the reporting details
        emit ProcessedConsensusLayerReport(_report, vars.trace);
    }

    /// @notice Retrieve the current epoch based on the current timestamp
    /// @param _cls The consensus layer spec struct
    /// @return The current epoch
    function _currentEpoch(CLSpec.CLSpecStruct memory _cls) internal view returns (uint256) {
        return ((block.timestamp - _cls.genesisTime) / _cls.secondsPerSlot) / _cls.slotsPerEpoch;
    }

    /// @notice Verifies if the given epoch is valid
    /// @param _cls The consensus layer spec struct
    /// @param _epoch The epoch to verify
    /// @return True if valid
    function _isValidEpoch(CLSpec.CLSpecStruct memory _cls, uint256 _epoch) internal view returns (bool) {
        return (
            _currentEpoch(_cls) >= _epoch + _cls.epochsToAssumedFinality
                && _epoch > LastConsensusLayerReport.get().epoch && _epoch % _cls.epochsPerFrame == 0
        );
    }

    /// @notice Retrieves the maximum increase in balance based on current total underlying supply and period since last report
    /// @param _rb The report bounds struct
    /// @param _prevTotalEth The total underlying supply during reporting
    /// @param _timeElapsed The time since last report
    /// @return The maximum allowed increase in balance
    function _maxIncrease(ReportBounds.ReportBoundsStruct memory _rb, uint256 _prevTotalEth, uint256 _timeElapsed)
        internal
        pure
        returns (uint256)
    {
        return (_prevTotalEth * _rb.annualAprUpperBound * _timeElapsed) / (LibBasisPoints.BASIS_POINTS_MAX * ONE_YEAR);
    }

    /// @notice Retrieves the maximum decrease in balance based on current total underlying supply
    /// @param _rb The report bounds struct
    /// @param _prevTotalEth The total underlying supply during reporting
    /// @return The maximum allowed decrease in balance
    function _maxDecrease(ReportBounds.ReportBoundsStruct memory _rb, uint256 _prevTotalEth)
        internal
        pure
        returns (uint256)
    {
        return (_prevTotalEth * _rb.relativeLowerBound) / LibBasisPoints.BASIS_POINTS_MAX;
    }

    /// @notice Retrieve the number of seconds between two epochs
    /// @param _cls The consensus layer spec struct
    /// @param _epochPast The starting epoch
    /// @param _epochNow The current epoch
    /// @return The number of seconds between the two epochs
    function _timeBetweenEpochs(CLSpec.CLSpecStruct memory _cls, uint256 _epochPast, uint256 _epochNow)
        internal
        pure
        returns (uint256)
    {
        return (_epochNow - _epochPast) * (_cls.secondsPerSlot * _cls.slotsPerEpoch);
    }
}

File 22 of 116 : 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 23 of 116 : 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 24 of 116 : AllowlistAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Allowlist Address Storage
/// @notice Utility to manage the Allowlist Address in storage
library AllowlistAddress {
    /// @notice Storage slot of the Allowlist Address
    bytes32 internal constant ALLOWLIST_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.allowlistAddress")) - 1);

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

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

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

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

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

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

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

File 26 of 116 : OperatorsRegistryAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Operators Registry Address Storage
/// @notice Utility to manage the Operators Registry Address in storage
library OperatorsRegistryAddress {
    /// @notice Storage slot of the Operators Registry Address
    bytes32 internal constant OPERATORS_REGISTRY_ADDRESS_SLOT =
        bytes32(uint256(keccak256("river.state.operatorsRegistryAddress")) - 1);

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

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

File 27 of 116 : CollectorAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Collector Address Storage
/// @notice Utility to manage the Collector Address in storage
library CollectorAddress {
    /// @notice Storage slot of the Collector Address
    bytes32 internal constant COLLECTOR_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.collectorAddress")) - 1);

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

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

File 28 of 116 : ELFeeRecipientAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Execution Layer Fee Recipient Address Storage
/// @notice Utility to manage the Execution Layer Fee Recipient Address in storage
library ELFeeRecipientAddress {
    /// @notice Storage slot of the Execution Layer Fee Recipient Address
    bytes32 internal constant EL_FEE_RECIPIENT_ADDRESS =
        bytes32(uint256(keccak256("river.state.elFeeRecipientAddress")) - 1);

    /// @notice Retrieve the Execution Layer Fee Recipient Address
    /// @return The Execution Layer Fee Recipient Address
    function get() internal view returns (address) {
        return LibUnstructuredStorage.getStorageAddress(EL_FEE_RECIPIENT_ADDRESS);
    }

    /// @notice Sets the Execution Layer Fee Recipient Address
    /// @param _newValue New Execution Layer Fee Recipient Address
    function set(address _newValue) internal {
        LibSanitize._notZeroAddress(_newValue);
        LibUnstructuredStorage.setStorageAddress(EL_FEE_RECIPIENT_ADDRESS, _newValue);
    }
}

File 29 of 116 : CoverageFundAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Coverage Fund Address Storage
/// @notice Utility to manage the Coverage Fund Address in storage
library CoverageFundAddress {
    /// @notice Storage slot of the Coverage Fund Address
    bytes32 internal constant COVERAGE_FUND_ADDRESS_SLOT =
        bytes32(uint256(keccak256("river.state.coverageFundAddress")) - 1);

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

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

File 30 of 116 : BalanceToRedeem.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

library BalanceToRedeem {
    bytes32 internal constant BALANCE_TO_REDEEM_SLOT = bytes32(uint256(keccak256("river.state.balanceToRedeem")) - 1);

    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(BALANCE_TO_REDEEM_SLOT);
    }

    function set(uint256 newValue) internal {
        LibUnstructuredStorage.setStorageUint256(BALANCE_TO_REDEEM_SLOT, newValue);
    }
}

File 31 of 116 : GlobalFee.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Global Fee Storage
/// @notice Utility to manage the Global Fee in storage
library GlobalFee {
    /// @notice Storage slot of the Global Fee
    bytes32 internal constant GLOBAL_FEE_SLOT = bytes32(uint256(keccak256("river.state.globalFee")) - 1);

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

    /// @notice Sets the Global Fee
    /// @param _newValue New Global Fee
    function set(uint256 _newValue) internal {
        LibSanitize._validFee(_newValue);
        LibUnstructuredStorage.setStorageUint256(GLOBAL_FEE_SLOT, _newValue);
    }
}

File 32 of 116 : MetadataURI.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Metadata URI Storage
/// @notice Utility to manage the Metadata in storage
library MetadataURI {
    /// @notice Storage slot of the Metadata URI
    bytes32 internal constant METADATA_URI_SLOT = bytes32(uint256(keccak256("river.state.metadataUri")) - 1);

    /// @notice Structure in storage
    struct Slot {
        /// @custom:attribute The metadata value
        string value;
    }

    /// @notice Retrieve the metadata URI
    /// @return The metadata URI string
    function get() internal view returns (string memory) {
        bytes32 slot = METADATA_URI_SLOT;

        Slot storage r;

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

        return r.value;
    }

    /// @notice Set the metadata URI value
    /// @param _newValue The new metadata URI value
    function set(string memory _newValue) internal {
        bytes32 slot = METADATA_URI_SLOT;

        Slot storage r;

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

        r.value = _newValue;
    }
}

File 33 of 116 : LastConsensusLayerReport.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../interfaces/components/IOracleManager.1.sol";

/// @title Last Consensus Layer Report Storage
/// @notice Utility to manage the Last Consensus Layer Report in storage
library LastConsensusLayerReport {
    /// @notice Storage slot of the Last Consensus Layer Report
    bytes32 internal constant LAST_CONSENSUS_LAYER_REPORT_SLOT =
        bytes32(uint256(keccak256("river.state.lastConsensusLayerReport")) - 1);

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

    /// @notice Retrieve the Last Consensus Layer Report from storage
    /// @return The Last Consensus Layer Report
    function get() internal view returns (IOracleManagerV1.StoredConsensusLayerReport storage) {
        bytes32 slot = LAST_CONSENSUS_LAYER_REPORT_SLOT;

        Slot storage r;

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

        return r.value;
    }

    /// @notice Set the Last Consensus Layer Report value in storage
    /// @param _newValue The new value to set in storage
    function set(IOracleManagerV1.StoredConsensusLayerReport memory _newValue) internal {
        bytes32 slot = LAST_CONSENSUS_LAYER_REPORT_SLOT;

        Slot storage r;

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

        r.value = _newValue;
    }
}

File 34 of 116 : Operators.2.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Operators Storage
/// @notice Utility to manage the Operators in storage
library OperatorsV2 {
    /// @notice Storage slot of the Operators
    bytes32 internal constant OPERATORS_SLOT = bytes32(uint256(keccak256("river.state.v2.operators")) - 1);

    /// @notice The Operator structure in storage
    struct Operator {
        /// @dev The following values respect this invariant:
        /// @dev     keys >= limit >= funded >= RequestedExits

        /// @custom:attribute Staking limit of the operator
        uint32 limit;
        /// @custom:attribute The count of funded validators
        uint32 funded;
        /// @custom:attribute The count of exit requests made to this operator
        uint32 requestedExits;
        /// @custom:attribute The total count of keys of the operator
        uint32 keys;
        /// @custom attribute The block at which the last edit happened in the operator details
        uint64 latestKeysEditBlockNumber;
        /// @custom:attribute True if the operator is active and allowed to operate on River
        bool active;
        /// @custom:attribute Display name of the operator
        string name;
        /// @custom:attribute Address of the operator
        address operator;
    }

    /// @notice The Operator structure when loaded in memory
    struct CachedOperator {
        /// @custom:attribute Staking limit of the operator
        uint32 limit;
        /// @custom:attribute The count of funded validators
        uint32 funded;
        /// @custom:attribute The count of exit requests made to this operator
        uint32 requestedExits;
        /// @custom:attribute The original index of the operator
        uint32 index;
        /// @custom:attribute The amount of picked keys, buffer used before changing funded in storage
        uint32 picked;
    }

    /// @notice The Operator structure when loaded in memory for the exit selection
    struct CachedExitableOperator {
        /// @custom:attribute The count of funded validators
        uint32 funded;
        /// @custom:attribute The count of exit requests made to this operator
        uint32 requestedExits;
        /// @custom:attribute The original index of the operator
        uint32 index;
        /// @custom:attribute The amount of picked keys, buffer used before changing funded in storage
        uint32 picked;
    }

    /// @notice The structure at the storage slot
    struct SlotOperator {
        /// @custom:attribute Array containing all the operators
        Operator[] value;
    }

    /// @notice The operator was not found
    /// @param index The provided index
    error OperatorNotFound(uint256 index);

    /// @notice Retrieve the operator in storage
    /// @param _index The index of the operator
    /// @return The Operator structure
    function get(uint256 _index) internal view returns (Operator storage) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        if (r.value.length <= _index) {
            revert OperatorNotFound(_index);
        }

        return r.value[_index];
    }

    /// @notice Retrieve the operators in storage
    /// @return The Operator structure array
    function getAll() internal view returns (Operator[] storage) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        return r.value;
    }

    /// @notice Retrieve the operator count in storage
    /// @return The count of operators in storage
    function getCount() internal view returns (uint256) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        return r.value.length;
    }

    /// @notice Retrieve all the active operators
    /// @return The list of active operator structures
    function getAllActive() internal view returns (Operator[] memory) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        uint256 activeCount = 0;
        uint256 operatorCount = r.value.length;

        for (uint256 idx = 0; idx < operatorCount;) {
            if (r.value[idx].active) {
                unchecked {
                    ++activeCount;
                }
            }
            unchecked {
                ++idx;
            }
        }

        Operator[] memory activeOperators = new Operator[](activeCount);

        uint256 activeIdx = 0;
        for (uint256 idx = 0; idx < operatorCount;) {
            if (r.value[idx].active) {
                activeOperators[activeIdx] = r.value[idx];
                unchecked {
                    ++activeIdx;
                }
            }
            unchecked {
                ++idx;
            }
        }

        return activeOperators;
    }

    /// @notice Retrieve the stopped validator count for an operator by its index
    /// @param stoppedValidatorCounts The storage pointer to the raw array containing the stopped validator counts
    /// @param index The index of the operator to lookup
    /// @return The amount of stopped validators for the given operator index
    function _getStoppedValidatorCountAtIndex(uint32[] storage stoppedValidatorCounts, uint256 index)
        internal
        view
        returns (uint32)
    {
        if (index + 1 >= stoppedValidatorCounts.length) {
            return 0;
        }
        return stoppedValidatorCounts[index + 1];
    }

    /// @notice Retrieve all the active and fundable operators
    /// @dev This method will return a memory array of length equal to the number of operator, but only
    /// @dev populated up to the fundable operator count, also returned by the method
    /// @return The list of active and fundable operators
    /// @return The count of active and fundable operators
    function getAllFundable() internal view returns (CachedOperator[] memory, uint256) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        uint256 fundableCount = 0;
        uint256 operatorCount = r.value.length;
        CachedOperator[] memory fundableOperators = new CachedOperator[](operatorCount);

        uint32[] storage stoppedValidatorCounts = getStoppedValidators();

        for (uint256 idx = 0; idx < operatorCount;) {
            if (
                _hasFundableKeys(r.value[idx])
                    && _getStoppedValidatorCountAtIndex(stoppedValidatorCounts, idx) >= r.value[idx].requestedExits
            ) {
                Operator storage op = r.value[idx];
                fundableOperators[fundableCount] = CachedOperator({
                    limit: op.limit,
                    funded: op.funded,
                    requestedExits: op.requestedExits,
                    index: uint32(idx),
                    picked: 0
                });
                unchecked {
                    ++fundableCount;
                }
            }
            unchecked {
                ++idx;
            }
        }

        return (fundableOperators, fundableCount);
    }

    /// @notice Retrieve all the active and exitable operators
    /// @dev This method will return a memory array of length equal to the number of operator, but only
    /// @dev populated up to the exitable operator count, also returned by the method
    /// @return The list of active and exitable operators
    /// @return The count of active and exitable operators
    function getAllExitable() internal view returns (CachedExitableOperator[] memory, uint256) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        uint256 exitableCount = 0;
        uint256 operatorCount = r.value.length;

        CachedExitableOperator[] memory exitableOperators = new CachedExitableOperator[](operatorCount);

        for (uint256 idx = 0; idx < operatorCount;) {
            if (_hasExitableKeys(r.value[idx])) {
                Operator storage op = r.value[idx];
                exitableOperators[exitableCount] = CachedExitableOperator({
                    funded: op.funded,
                    requestedExits: op.requestedExits,
                    index: uint32(idx),
                    picked: 0
                });
                unchecked {
                    ++exitableCount;
                }
            }
            unchecked {
                ++idx;
            }
        }

        return (exitableOperators, exitableCount);
    }

    /// @notice Add a new operator in storage
    /// @param _newOperator Value of the new operator
    /// @return The size of the operator array after the operation
    function push(Operator memory _newOperator) internal returns (uint256) {
        LibSanitize._notZeroAddress(_newOperator.operator);
        LibSanitize._notEmptyString(_newOperator.name);
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        r.value.push(_newOperator);

        return r.value.length;
    }

    /// @notice Atomic operation to set the key count and update the latestKeysEditBlockNumber field at the same time
    /// @param _index The operator index
    /// @param _newKeys The new value for the key count
    function setKeys(uint256 _index, uint32 _newKeys) internal {
        Operator storage op = get(_index);

        op.keys = _newKeys;
        op.latestKeysEditBlockNumber = uint64(block.number);
    }

    /// @notice Checks if an operator is active and has fundable keys
    /// @param _operator The operator details
    /// @return True if active and fundable
    function _hasFundableKeys(OperatorsV2.Operator memory _operator) internal pure returns (bool) {
        return (_operator.active && _operator.limit > _operator.funded);
    }

    /// @notice Checks if an operator is active and has exitable keys
    /// @param _operator The operator details
    /// @return True if active and exitable
    function _hasExitableKeys(OperatorsV2.Operator memory _operator) internal pure returns (bool) {
        return (_operator.active && _operator.funded > _operator.requestedExits);
    }

    /// @notice Storage slot of the Stopped Validators
    bytes32 internal constant STOPPED_VALIDATORS_SLOT = bytes32(uint256(keccak256("river.state.stoppedValidators")) - 1);

    struct SlotStoppedValidators {
        uint32[] value;
    }

    /// @notice Retrieve the storage pointer of the Stopped Validators array
    /// @return The Stopped Validators storage pointer
    function getStoppedValidators() internal view returns (uint32[] storage) {
        bytes32 slot = STOPPED_VALIDATORS_SLOT;

        SlotStoppedValidators storage r;

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

        return r.value;
    }

    /// @notice Sets the entire stopped validators array
    /// @param value The new stopped validators array
    function setRawStoppedValidators(uint32[] memory value) internal {
        bytes32 slot = STOPPED_VALIDATORS_SLOT;

        SlotStoppedValidators storage r;

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

        r.value = value;
    }
}

File 35 of 116 : 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 36 of 116 : 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 37 of 116 : 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 38 of 116 : 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 39 of 116 : 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 40 of 116 : 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 41 of 116 : 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 42 of 116 : 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);
}

File 43 of 116 : IDepositContract.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Deposit Contract Interface
/// @notice This interface exposes methods to perform validator deposits
interface IDepositContract {
    /// @notice Official deposit method to activate a validator on the consensus layer
    /// @param pubkey The 48 bytes long BLS Public key representing the validator
    /// @param withdrawalCredentials The 32 bytes long withdrawal credentials, configures the withdrawal recipient
    /// @param signature The 96 bytes long BLS Signature performed by the pubkey's private key
    /// @param depositDataRoot The root hash of the whole deposit data structure
    function deposit(
        bytes calldata pubkey,
        bytes calldata withdrawalCredentials,
        bytes calldata signature,
        bytes32 depositDataRoot
    ) external payable;
}

File 44 of 116 : LibBytes.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

/// @title Lib Bytes
/// @notice This library helps manipulating bytes
library LibBytes {
    /// @notice The length overflows an uint
    error SliceOverflow();

    /// @notice The slice is outside of the initial bytes bounds
    error SliceOutOfBounds();

    /// @notice Slices the provided bytes
    /// @param _bytes Bytes to slice
    /// @param _start The starting index of the slice
    /// @param _length The length of the slice
    /// @return The slice of _bytes starting at _start of length _length
    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        unchecked {
            if (_length + 31 < _length) {
                revert SliceOverflow();
            }
        }
        if (_bytes.length < _start + _length) {
            revert SliceOutOfBounds();
        }

        bytes memory tempBytes;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }
}

File 45 of 116 : 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 46 of 116 : DepositContractAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Deposit Contract Address Storage
/// @notice Utility to manage the Deposit Contract Address in storage
library DepositContractAddress {
    /// @notice Storage slot of the Deposit Contract Address
    bytes32 internal constant DEPOSIT_CONTRACT_ADDRESS_SLOT =
        bytes32(uint256(keccak256("river.state.depositContractAddress")) - 1);

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

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

File 47 of 116 : WithdrawalCredentials.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Withdrawal Credentials Storage
/// @notice Utility to manage the Withdrawal Credentials in storage
library WithdrawalCredentials {
    /// @notice Storage slot of the Withdrawal Credentials
    bytes32 internal constant WITHDRAWAL_CREDENTIALS_SLOT =
        bytes32(uint256(keccak256("river.state.withdrawalCredentials")) - 1);

    /// @notice Retrieve the Withdrawal Credentials
    /// @return The Withdrawal Credentials
    function get() internal view returns (bytes32) {
        return LibUnstructuredStorage.getStorageBytes32(WITHDRAWAL_CREDENTIALS_SLOT);
    }

    /// @notice Retrieve the Withdrawal Credential under its address format
    /// @return The Withdrawal Credentials in its address format
    function getAddress() internal view returns (address) {
        return LibUnstructuredStorage.getStorageAddress(WITHDRAWAL_CREDENTIALS_SLOT);
    }

    /// @notice Sets the Withdrawal Credentials
    /// @param _newValue New Withdrawal Credentials
    function set(bytes32 _newValue) internal {
        if (_newValue == bytes32(0)) {
            revert LibErrors.InvalidArgument();
        }
        LibUnstructuredStorage.setStorageBytes32(WITHDRAWAL_CREDENTIALS_SLOT, _newValue);
    }
}

File 48 of 116 : DepositedValidatorCount.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Deposited Validator Count Storage
/// @notice Utility to manage the Deposited Validator Count in storage
library DepositedValidatorCount {
    /// @notice Storage slot of the Deposited Validator Count
    bytes32 internal constant DEPOSITED_VALIDATOR_COUNT_SLOT =
        bytes32(uint256(keccak256("river.state.depositedValidatorCount")) - 1);

    /// @notice Retrieve the Deposited Validator Count
    /// @return The Deposited Validator Count
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(DEPOSITED_VALIDATOR_COUNT_SLOT);
    }

    /// @notice Sets the Deposited Validator Count
    /// @param _newValue New Deposited Validator Count
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(DEPOSITED_VALIDATOR_COUNT_SLOT, _newValue);
    }
}

File 49 of 116 : BalanceToDeposit.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

library BalanceToDeposit {
    bytes32 internal constant BALANCE_TO_DEPOSIT_SLOT = bytes32(uint256(keccak256("river.state.balanceToDeposit")) - 1);

    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(BALANCE_TO_DEPOSIT_SLOT);
    }

    function set(uint256 newValue) internal {
        LibUnstructuredStorage.setStorageUint256(BALANCE_TO_DEPOSIT_SLOT, newValue);
    }
}

File 50 of 116 : CommittedBalance.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

library CommittedBalance {
    bytes32 internal constant COMMITTED_BALANCE_SLOT = bytes32(uint256(keccak256("river.state.committedBalance")) - 1);

    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(COMMITTED_BALANCE_SLOT);
    }

    function set(uint256 newValue) internal {
        LibUnstructuredStorage.setStorageUint256(COMMITTED_BALANCE_SLOT, newValue);
    }
}

File 51 of 116 : Shares.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Shares Count Storage
/// @notice Utility to manage the Shares Count in storage
library Shares {
    /// @notice Storage slot of the Shares Count
    bytes32 internal constant SHARES_SLOT = bytes32(uint256(keccak256("river.state.shares")) - 1);

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

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

File 52 of 116 : SharesPerOwner.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Shares Per Owner Storage
/// @notice Utility to manage the Shares Per Owner in storage
library SharesPerOwner {
    /// @notice Storage slot of the Shares Per Owner
    bytes32 internal constant SHARES_PER_OWNER_SLOT = bytes32(uint256(keccak256("river.state.sharesPerOwner")) - 1);

    /// @notice Structure in storage
    struct Slot {
        /// @custom:attribute The mapping from an owner to its share count
        mapping(address => uint256) value;
    }

    /// @notice Retrieve the share count for given owner
    /// @param _owner The address to get the balance of
    /// @return The amount of shares
    function get(address _owner) internal view returns (uint256) {
        bytes32 slot = SHARES_PER_OWNER_SLOT;

        Slot storage r;

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

        return r.value[_owner];
    }

    /// @notice Set the amount of shares for an owner
    /// @param _owner The owner of the shares to edit
    /// @param _newValue The new shares value for the owner
    function set(address _owner, uint256 _newValue) internal {
        bytes32 slot = SHARES_PER_OWNER_SLOT;

        Slot storage r;

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

        r.value[_owner] = _newValue;
    }
}

File 53 of 116 : ApprovalsPerOwner.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Approvals Per Owner Storage
/// @notice Utility to manage the Approvals Per Owner in storage
library ApprovalsPerOwner {
    /// @notice Storage slot of the Approvals Per Owner
    bytes32 internal constant APPROVALS_PER_OWNER_SLOT =
        bytes32(uint256(keccak256("river.state.approvalsPerOwner")) - 1);

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The mapping from an owner to an operator to the approval amount
        mapping(address => mapping(address => uint256)) value;
    }

    /// @notice Retrieve the approval for an owner to an operator
    /// @param _owner The account that gave the approval
    /// @param _operator The account receiving the approval
    /// @return The value of the approval
    function get(address _owner, address _operator) internal view returns (uint256) {
        bytes32 slot = APPROVALS_PER_OWNER_SLOT;

        Slot storage r;

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

        return r.value[_owner][_operator];
    }

    /// @notice Set the approval value for an owner to an operator
    /// @param _owner The account that gives the approval
    /// @param _operator The account receiving the approval
    /// @param _newValue The value of the approval
    function set(address _owner, address _operator, uint256 _newValue) internal {
        bytes32 slot = APPROVALS_PER_OWNER_SLOT;

        Slot storage r;

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

        r.value[_owner][_operator] = _newValue;
    }
}

File 54 of 116 : 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 55 of 116 : OracleAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Oracle Address Storage
/// @notice Utility to manage the Oracle Address in storage
library OracleAddress {
    /// @notice Storage slot of the Oracle Address
    bytes32 internal constant ORACLE_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.oracleAddress")) - 1);

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

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

File 56 of 116 : CLValidatorTotalBalance.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Consensus Layer Validator Total Balance Storage
/// @notice Utility to manage the Consensus Layer Validator Total Balance in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library CLValidatorTotalBalance {
    /// @notice Storage slot of the Consensus Layer Validator Total Balance
    bytes32 internal constant CL_VALIDATOR_TOTAL_BALANCE_SLOT =
        bytes32(uint256(keccak256("river.state.clValidatorTotalBalance")) - 1);

    /// @notice Retrieve the Consensus Layer Validator Total Balance
    /// @return The Consensus Layer Validator Total Balance
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(CL_VALIDATOR_TOTAL_BALANCE_SLOT);
    }

    /// @notice Sets the Consensus Layer Validator Total Balance
    /// @param _newValue New Consensus Layer Validator Total Balance
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(CL_VALIDATOR_TOTAL_BALANCE_SLOT, _newValue);
    }
}

File 57 of 116 : CLValidatorCount.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Consensus Layer Validator Count Storage
/// @notice Utility to manage the Consensus Layer Validator Count in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library CLValidatorCount {
    /// @notice Storage slot of the Consensus Layer Validator Count
    bytes32 internal constant CL_VALIDATOR_COUNT_SLOT = bytes32(uint256(keccak256("river.state.clValidatorCount")) - 1);

    /// @notice Retrieve the Consensus Layer Validator Count
    /// @return The Consensus Layer Validator Count
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(CL_VALIDATOR_COUNT_SLOT);
    }

    /// @notice Sets the Consensus Layer Validator Count
    /// @param _newValue New Consensus Layer Validator Count
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(CL_VALIDATOR_COUNT_SLOT, _newValue);
    }
}

File 58 of 116 : LastOracleRoundId.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Last Oracle Round Id Storage
/// @notice Utility to manage the Last Oracle Round Id in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library LastOracleRoundId {
    /// @notice Storage slot of the Last Oracle Round Id
    bytes32 internal constant LAST_ORACLE_ROUND_ID_SLOT =
        bytes32(uint256(keccak256("river.state.lastOracleRoundId")) - 1);

    /// @notice Retrieve the Last Oracle Round Id
    /// @return The Last Oracle Round Id
    function get() internal view returns (bytes32) {
        return LibUnstructuredStorage.getStorageBytes32(LAST_ORACLE_ROUND_ID_SLOT);
    }

    /// @notice Sets the Last Oracle Round Id
    /// @param _newValue New Last Oracle Round Id
    function set(bytes32 _newValue) internal {
        LibUnstructuredStorage.setStorageBytes32(LAST_ORACLE_ROUND_ID_SLOT, _newValue);
    }
}

File 59 of 116 : 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 60 of 116 : 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 61 of 116 : 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 62 of 116 : WLSETH.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";

import "./interfaces/IRiver.1.sol";
import "./interfaces/IWLSETH.1.sol";

import "./Initializable.sol";

import "./state/shared/RiverAddress.sol";
import "./state/shared/ApprovalsPerOwner.sol";
import "./state/wlseth/BalanceOf.sol";

/// @title Wrapped LsETH (v1)
/// @author Kiln
/// @notice This contract wraps the LsETH token into a rebase token, more suitable for some DeFi use-cases
///         like stable swaps.
contract WLSETHV1 is IWLSETHV1, Initializable, ReentrancyGuard {
    /// @notice Ensures that the value is not 0
    /// @param _value Value that must be > 0
    modifier isNotNull(uint256 _value) {
        if (_value == 0) {
            revert NullTransfer();
        }
        _;
    }

    /// @notice Ensures that the owner has enough funds
    /// @param _owner Owner of the balance to verify
    /// @param _value Minimum required value
    modifier hasFunds(address _owner, uint256 _value) {
        if (_balanceOf(_owner) < _value) {
            revert BalanceTooLow();
        }
        _;
    }

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

    /// @inheritdoc IWLSETHV1
    function name() external pure returns (string memory) {
        return "Wrapped Liquid Staked ETH";
    }

    /// @inheritdoc IWLSETHV1
    function symbol() external pure returns (string memory) {
        return "wLsETH";
    }

    /// @inheritdoc IWLSETHV1
    function decimals() external pure returns (uint8) {
        return 18;
    }

    /// @inheritdoc IWLSETHV1
    function totalSupply() external view returns (uint256) {
        return IRiverV1(payable(RiverAddress.get())).balanceOfUnderlying(address(this));
    }

    /// @inheritdoc IWLSETHV1
    function balanceOf(address _owner) external view returns (uint256) {
        return _balanceOf(_owner);
    }

    /// @inheritdoc IWLSETHV1
    function sharesOf(address _owner) external view returns (uint256) {
        return BalanceOf.get(_owner);
    }

    /// @inheritdoc IWLSETHV1
    function allowance(address _owner, address _spender) external view returns (uint256) {
        return ApprovalsPerOwner.get(_owner, _spender);
    }

    /// @inheritdoc IWLSETHV1
    function transfer(address _to, uint256 _value)
        external
        isNotNull(_value)
        hasFunds(msg.sender, _value)
        returns (bool)
    {
        if (_to == address(0)) {
            revert UnauthorizedTransfer(msg.sender, address(0));
        }
        return _transfer(msg.sender, _to, _value);
    }

    /// @inheritdoc IWLSETHV1
    function transferFrom(address _from, address _to, uint256 _value)
        external
        isNotNull(_value)
        hasFunds(_from, _value)
        returns (bool)
    {
        if (_to == address(0)) {
            revert UnauthorizedTransfer(_from, address(0));
        }
        _spendAllowance(_from, _value);
        return _transfer(_from, _to, _value);
    }

    /// @inheritdoc IWLSETHV1
    function approve(address _spender, uint256 _value) external returns (bool) {
        _approve(msg.sender, _spender, _value);
        return true;
    }

    /// @inheritdoc IWLSETHV1
    function increaseAllowance(address _spender, uint256 _additionalValue) external returns (bool) {
        _approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) + _additionalValue);
        return true;
    }

    /// @inheritdoc IWLSETHV1
    function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool) {
        _approve(msg.sender, _spender, ApprovalsPerOwner.get(msg.sender, _spender) - _subtractableValue);
        return true;
    }

    /// @inheritdoc IWLSETHV1
    function mint(address _recipient, uint256 _shares) external nonReentrant {
        BalanceOf.set(_recipient, BalanceOf.get(_recipient) + _shares);
        IRiverV1 river = IRiverV1(payable(RiverAddress.get()));
        if (!river.transferFrom(msg.sender, address(this), _shares)) {
            revert TokenTransferError();
        }
        emit Mint(_recipient, _shares);
        emit Transfer(address(0), _recipient, river.underlyingBalanceFromShares(_shares));
    }

    /// @inheritdoc IWLSETHV1
    function burn(address _recipient, uint256 _shares) external nonReentrant {
        uint256 shares = BalanceOf.get(msg.sender);
        if (_shares > shares) {
            revert BalanceTooLow();
        }
        BalanceOf.set(msg.sender, shares - _shares);
        IRiverV1 river = IRiverV1(payable(RiverAddress.get()));
        if (!river.transfer(_recipient, _shares)) {
            revert TokenTransferError();
        }
        emit Transfer(msg.sender, address(0), river.underlyingBalanceFromShares(_shares));
        emit Burn(_recipient, _shares);
    }

    /// @notice Internal utility to spend the allowance of an account from the message sender
    /// @param _from Address owning the allowance
    /// @param _value Amount of allowance to spend
    function _spendAllowance(address _from, uint256 _value) internal {
        uint256 currentAllowance = ApprovalsPerOwner.get(_from, msg.sender);
        if (currentAllowance < _value) {
            revert AllowanceTooLow(_from, msg.sender, currentAllowance, _value);
        }
        if (currentAllowance != type(uint256).max) {
            _approve(_from, msg.sender, currentAllowance - _value);
        }
    }

    /// @notice Internal utility to change the allowance of an owner to a spender
    /// @param _owner The owner of the wrapped tokens
    /// @param _spender The allowed spender of the wrapped tokens
    /// @param _value The new allowance value
    function _approve(address _owner, address _spender, uint256 _value) internal {
        LibSanitize._notZeroAddress(_owner);
        LibSanitize._notZeroAddress(_spender);
        ApprovalsPerOwner.set(_owner, _spender, _value);
        emit Approval(_owner, _spender, _value);
    }

    /// @notice Internal utility to retrieve the amount of token per owner
    /// @param _owner Account to be checked
    /// @return The balance of the account
    function _balanceOf(address _owner) internal view returns (uint256) {
        return IRiverV1(payable(RiverAddress.get())).underlyingBalanceFromShares(BalanceOf.get(_owner));
    }

    /// @notice Internal utility to perform an unchecked transfer
    /// @param _from Address sending the tokens
    /// @param _to Address receiving the tokens
    /// @param _value Amount to be sent
    /// @return True if success
    function _transfer(address _from, address _to, uint256 _value) internal returns (bool) {
        uint256 valueToShares = IRiverV1(payable(RiverAddress.get())).sharesFromUnderlyingBalance(_value);
        BalanceOf.set(_from, BalanceOf.get(_from) - valueToShares);
        BalanceOf.set(_to, BalanceOf.get(_to) + valueToShares);

        emit Transfer(_from, _to, _value);

        return true;
    }
}

File 63 of 116 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 64 of 116 : IWLSETH.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Wrapped LsETH Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to wrap the LsETH token into a rebase token.
interface IWLSETHV1 {
    /// @notice A transfer has been made
    /// @param from The transfer sender
    /// @param to The transfer recipient
    /// @param value The amount transfered
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice An approval has been made
    /// @param owner The token owner
    /// @param spender The account allowed by the owner
    /// @param value The amount allowed
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Tokens have been minted
    /// @param recipient The account receiving the new tokens
    /// @param shares The amount of LsETH provided
    event Mint(address indexed recipient, uint256 shares);

    /// @notice Tokens have been burned
    /// @param recipient The account that receive the underlying LsETH
    /// @param shares The amount of LsETH that got sent back
    event Burn(address indexed recipient, uint256 shares);

    /// @notice The stored value of river has been changed
    /// @param river The new address of river
    event SetRiver(address indexed river);

    /// @notice The token transfer failed during the minting or burning process
    error TokenTransferError();

    /// @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
    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 Initializes the wrapped token contract
    /// @param _river Address of the River contract
    function initWLSETHV1(address _river) external;

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

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

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

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

    /// @notice Retrieves the token balance of the specified user
    /// @param _owner Owner to check the balance
    /// @return The balance of the owner
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Retrieves the raw shares count of the user
    /// @param _owner Owner to check the shares balance
    /// @return The shares of the owner
    function sharesOf(address _owner) external view returns (uint256);

    /// @notice Retrieves the token allowance given from one address to another
    /// @param _owner Owner that gave the allowance
    /// @param _spender Spender that received the allowance
    /// @return The allowance of the owner to the spender
    function allowance(address _owner, address _spender) external view returns (uint256);

    /// @notice Transfers tokens between the message sender and a recipient
    /// @param _to Recipient of the transfer
    /// @param _value Amount to transfer
    /// @return True if success
    function transfer(address _to, uint256 _value) external returns (bool);

    /// @notice Transfers tokens between two accounts
    /// @dev It is expected that _from has given at least _value allowance to msg.sender
    /// @param _from Sender account
    /// @param _to Recipient of the transfer
    /// @param _value Amount to transfer
    /// @return True if success
    function transferFrom(address _from, address _to, uint256 _value) external returns (bool);

    /// @notice Approves another account to transfer tokens
    /// @param _spender Spender that receives the allowance
    /// @param _value Amount to allow
    /// @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 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 to subtract
    /// @return True if success
    function decreaseAllowance(address _spender, uint256 _subtractableValue) external returns (bool);

    /// @notice Mint tokens by providing LsETH tokens
    /// @dev The message sender locks LsETH tokens and received wrapped LsETH tokens in exchange
    /// @dev The message sender needs to approve the contract to mint the wrapped tokens
    /// @dev The minted wrapped LsETH is sent to the specified recipient
    /// @param _recipient The account receiving the new minted wrapped LsETH
    /// @param _shares The amount of LsETH to wrap
    function mint(address _recipient, uint256 _shares) external;

    /// @notice Burn tokens and retrieve underlying LsETH tokens
    /// @dev The message sender burns shares from its balance for the LsETH equivalent value
    /// @dev The message sender doesn't need to approve the contract to burn the shares
    /// @dev The freed LsETH is sent to the specified recipient
    /// @param _recipient The account receiving the underlying LsETH tokens after shares are burned
    /// @param _shares Amount of LsETH to free by burning wrapped LsETH
    function burn(address _recipient, uint256 _shares) external;
}

File 65 of 116 : 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 66 of 116 : BalanceOf.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Balance Storage
/// @notice Utility to manage the Balance in storage
library BalanceOf {
    /// @notice Storage slot of the Balance
    bytes32 internal constant BALANCE_OF_SLOT = bytes32(uint256(keccak256("river.state.balanceOf")) - 1);

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The mapping from an owner to its balance
        mapping(address => uint256) value;
    }

    /// @notice Retrieve balance of an owner
    /// @param _owner The owner of the balance
    /// @return The balance of the owner
    function get(address _owner) internal view returns (uint256) {
        bytes32 slot = BALANCE_OF_SLOT;

        Slot storage r;

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

        return r.value[_owner];
    }

    /// @notice Set the balance of an owner
    /// @param _owner The owner to change the balance of
    /// @param _newValue New balance value for the owner
    function set(address _owner, uint256 _newValue) internal {
        bytes32 slot = BALANCE_OF_SLOT;

        Slot storage r;

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

        r.value[_owner] = _newValue;
    }
}

File 67 of 116 : OracleMembers.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Oracle Members Storage
/// @notice Utility to manage the Oracle Members in storage
/// @dev There can only be up to 256 oracle members. This is due to how report statuses are stored in Reports Positions
library OracleMembers {
    /// @notice Storage slot of the Oracle Members
    bytes32 internal constant ORACLE_MEMBERS_SLOT = bytes32(uint256(keccak256("river.state.oracleMembers")) - 1);

    /// @notice The structure in storage
    struct Slot {
        /// @custom:attribute The array of oracle members
        address[] value;
    }

    /// @notice Retrieve the list of oracle members
    /// @return List of oracle members
    function get() internal view returns (address[] memory) {
        bytes32 slot = ORACLE_MEMBERS_SLOT;

        Slot storage r;

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

        return r.value;
    }

    /// @notice Add a new oracle member to the list
    /// @param _newOracleMember Address of the new oracle member
    function push(address _newOracleMember) internal {
        LibSanitize._notZeroAddress(_newOracleMember);

        bytes32 slot = ORACLE_MEMBERS_SLOT;

        Slot storage r;

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

        r.value.push(_newOracleMember);
    }

    /// @notice Set an address in the oracle member list
    /// @param _index The index to edit
    /// @param _newOracleAddress The new value of the oracle member
    function set(uint256 _index, address _newOracleAddress) internal {
        bytes32 slot = ORACLE_MEMBERS_SLOT;

        Slot storage r;

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

        r.value[_index] = _newOracleAddress;
    }

    /// @notice Retrieve the index of the oracle member
    /// @param _memberAddress The address to lookup
    /// @return The index of the member, -1 if not found
    function indexOf(address _memberAddress) internal view returns (int256) {
        bytes32 slot = ORACLE_MEMBERS_SLOT;

        Slot storage r;

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

        for (uint256 idx = 0; idx < r.value.length;) {
            if (r.value[idx] == _memberAddress) {
                return int256(idx);
            }
            unchecked {
                ++idx;
            }
        }

        return int256(-1);
    }

    /// @notice Delete the oracle member at the given index
    /// @param _idx The index of the member to remove
    function deleteItem(uint256 _idx) internal {
        bytes32 slot = ORACLE_MEMBERS_SLOT;

        Slot storage r;

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

        uint256 lastIdx = r.value.length - 1;
        if (lastIdx != _idx) {
            r.value[_idx] = r.value[lastIdx];
        }

        r.value.pop();
    }
}

File 68 of 116 : Oracle.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IRiver.1.sol";
import "./interfaces/IOracle.1.sol";

import "./Administrable.sol";
import "./Initializable.sol";

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

import "./state/oracle/LastEpochId.sol";
import "./state/oracle/OracleMembers.sol";
import "./state/oracle/Quorum.sol";
import "./state/oracle/ReportsPositions.sol";

/// @title Oracle (v1)
/// @author Kiln
/// @notice This contract handles the input from the allowed oracle members. Highly inspired by Lido's implementation.
contract OracleV1 is IOracleV1, Initializable, Administrable {
    modifier onlyAdminOrMember(address _oracleMember) {
        if (msg.sender != _getAdmin() && msg.sender != _oracleMember) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @inheritdoc IOracleV1
    function initOracleV1(
        address _riverAddress,
        address _administratorAddress,
        uint64 _epochsPerFrame,
        uint64 _slotsPerEpoch,
        uint64 _secondsPerSlot,
        uint64 _genesisTime,
        uint256 _annualAprUpperBound,
        uint256 _relativeLowerBound
    ) external init(0) {
        _setAdmin(_administratorAddress);
        RiverAddress.set(_riverAddress);
        emit SetRiver(_riverAddress);
        CLSpec.set(
            CLSpec.CLSpecStruct({
                epochsPerFrame: _epochsPerFrame,
                slotsPerEpoch: _slotsPerEpoch,
                secondsPerSlot: _secondsPerSlot,
                genesisTime: _genesisTime,
                epochsToAssumedFinality: 0
            })
        );
        emit SetSpec(_epochsPerFrame, _slotsPerEpoch, _secondsPerSlot, _genesisTime);
        ReportBounds.set(
            ReportBounds.ReportBoundsStruct({
                annualAprUpperBound: _annualAprUpperBound,
                relativeLowerBound: _relativeLowerBound
            })
        );
        emit SetBounds(_annualAprUpperBound, _relativeLowerBound);
        Quorum.set(0);
        emit SetQuorum(0);
    }

    /// @inheritdoc IOracleV1
    function initOracleV1_1() external init(1) {
        _clearReports();
    }

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

    /// @inheritdoc IOracleV1
    function getMemberReportStatus(address _oracleMember) external view returns (bool) {
        int256 memberIndex = OracleMembers.indexOf(_oracleMember);
        return memberIndex != -1 && ReportsPositions.get(uint256(memberIndex));
    }

    /// @inheritdoc IOracleV1
    function getGlobalReportStatus() external view returns (uint256) {
        return ReportsPositions.getRaw();
    }

    /// @inheritdoc IOracleV1
    function getReportVariantsCount() external view returns (uint256) {
        return ReportsVariants.get().length;
    }

    /// @inheritdoc IOracleV1
    function getReportVariantDetails(uint256 _idx)
        external
        view
        returns (ReportsVariants.ReportVariantDetails memory)
    {
        if (ReportsVariants.get().length <= _idx) {
            revert ReportIndexOutOfBounds(_idx, ReportsVariants.get().length);
        }
        return ReportsVariants.get()[_idx];
    }

    /// @inheritdoc IOracleV1
    function getQuorum() external view returns (uint256) {
        return Quorum.get();
    }

    /// @inheritdoc IOracleV1
    function getOracleMembers() external view returns (address[] memory) {
        return OracleMembers.get();
    }

    /// @inheritdoc IOracleV1
    function isMember(address _memberAddress) external view returns (bool) {
        return OracleMembers.indexOf(_memberAddress) >= 0;
    }

    /// @inheritdoc IOracleV1
    function getLastReportedEpochId() external view returns (uint256) {
        return LastEpochId.get();
    }

    /// @inheritdoc IOracleV1
    function addMember(address _newOracleMember, uint256 _newQuorum) external onlyAdmin {
        int256 memberIdx = OracleMembers.indexOf(_newOracleMember);
        if (memberIdx >= 0) {
            revert AddressAlreadyInUse(_newOracleMember);
        }
        OracleMembers.push(_newOracleMember);
        uint256 previousQuorum = Quorum.get();
        _clearReportsAndSetQuorum(_newQuorum, previousQuorum);
        emit AddMember(_newOracleMember);
    }

    /// @inheritdoc IOracleV1
    function removeMember(address _oracleMember, uint256 _newQuorum) external onlyAdmin {
        int256 memberIdx = OracleMembers.indexOf(_oracleMember);
        if (memberIdx < 0) {
            revert LibErrors.InvalidCall();
        }
        OracleMembers.deleteItem(uint256(memberIdx));
        uint256 previousQuorum = Quorum.get();
        _clearReportsAndSetQuorum(_newQuorum, previousQuorum);
        emit RemoveMember(_oracleMember);
    }

    /// @inheritdoc IOracleV1
    function setMember(address _oracleMember, address _newAddress) external onlyAdminOrMember(_oracleMember) {
        LibSanitize._notZeroAddress(_newAddress);
        if (OracleMembers.indexOf(_newAddress) >= 0) {
            revert AddressAlreadyInUse(_newAddress);
        }
        int256 memberIdx = OracleMembers.indexOf(_oracleMember);
        if (memberIdx < 0) {
            revert LibErrors.InvalidCall();
        }
        OracleMembers.set(uint256(memberIdx), _newAddress);
        emit SetMember(_oracleMember, _newAddress);
    }

    /// @inheritdoc IOracleV1
    function setQuorum(uint256 _newQuorum) external onlyAdmin {
        uint256 previousQuorum = Quorum.get();
        if (previousQuorum == _newQuorum) {
            revert LibErrors.InvalidArgument();
        }
        _clearReportsAndSetQuorum(_newQuorum, previousQuorum);
    }

    /// @inheritdoc IOracleV1
    function reportConsensusLayerData(IRiverV1.ConsensusLayerReport calldata _report) external {
        // retrieve member index and revert if not oracle member
        int256 memberIndex = OracleMembers.indexOf(msg.sender);
        if (memberIndex == -1) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        // store last reported epoch to stack
        uint256 lastReportedEpochValue = LastEpochId.get();

        // checks that the report epoch is not too old
        if (_report.epoch < lastReportedEpochValue) {
            revert EpochTooOld(_report.epoch, LastEpochId.get());
        }
        IRiverV1 river = IRiverV1(payable(RiverAddress.get()));
        // checks that the report epoch is not invalid
        if (!river.isValidEpoch(_report.epoch)) {
            revert InvalidEpoch(_report.epoch);
        }
        // if valid and greater than the lastReportedEpoch, we clear the reporting data
        if (_report.epoch > lastReportedEpochValue) {
            _clearReports();
            LastEpochId.set(_report.epoch);
            emit SetLastReportedEpoch(_report.epoch);
        }
        // we retrieve the voting status of the caller, and revert if already voted
        if (ReportsPositions.get(uint256(memberIndex))) {
            revert AlreadyReported(_report.epoch, msg.sender);
        }
        // we register the caller
        ReportsPositions.register(uint256(memberIndex));

        // we compute the variant by hashing the report
        bytes32 variant = _reportChecksum(_report);
        // we retrieve the details for the given variant
        (int256 variantIndex, uint256 variantVotes) = _getReportVariantIndexAndVotes(variant);
        // we retrieve the quorum to stack
        uint256 quorum = Quorum.get();

        emit ReportedConsensusLayerData(msg.sender, variant, _report, variantVotes + 1, quorum);

        // if adding this vote reaches quorum
        if (variantVotes + 1 >= quorum) {
            // we clear the reporting data
            _clearReports();
            // we increment the lastReportedEpoch to force reports to be on the last frame
            LastEpochId.set(_report.epoch + 1);
            // we push the report to river
            river.setConsensusLayerData(_report);
            emit SetLastReportedEpoch(_report.epoch + 1);
        } else if (variantVotes == 0) {
            // if we have no votes for the variant, we create the variant details
            ReportsVariants.push(ReportsVariants.ReportVariantDetails({variant: variant, votes: 1}));
        } else {
            // otherwise we increment the vote
            ReportsVariants.get()[uint256(variantIndex)].votes += 1;
        }
    }

    /// @notice Internal utility to clear all the reports and edit the quorum if a new value is provided
    /// @dev Ensures that the quorum respects invariants
    /// @dev The admin is in charge of providing a proper quorum based on the oracle member count
    /// @dev The quorum value Q should respect the following invariant, where O is oracle member count
    /// @dev (O / 2) + 1 <= Q <= O
    /// @param _newQuorum New quorum value
    /// @param _previousQuorum The old quorum value
    function _clearReportsAndSetQuorum(uint256 _newQuorum, uint256 _previousQuorum) internal {
        uint256 memberCount = OracleMembers.get().length;
        if ((_newQuorum == 0 && memberCount > 0) || _newQuorum > memberCount) {
            revert LibErrors.InvalidArgument();
        }
        _clearReports();
        if (_newQuorum != _previousQuorum) {
            Quorum.set(_newQuorum);
            emit SetQuorum(_newQuorum);
        }
    }

    /// @notice Internal utility to hash and retrieve the variant id of a report
    /// @param _report The reported data structure
    /// @return The report variant
    function _reportChecksum(IRiverV1.ConsensusLayerReport calldata _report) internal pure returns (bytes32) {
        return keccak256(abi.encode(_report));
    }

    /// @notice Internal utility to clear all reporting details
    function _clearReports() internal {
        ReportsVariants.clear();
        ReportsPositions.clear();
        emit ClearedReporting();
    }

    /// @notice Internal utility to retrieve index and vote count for a given variant
    /// @param _variant The variant to lookup
    /// @return The index of the variant, -1 if not found
    /// @return The vote count of the variant
    function _getReportVariantIndexAndVotes(bytes32 _variant) internal view returns (int256, uint256) {
        uint256 reportVariantsLength = ReportsVariants.get().length;
        for (uint256 idx = 0; idx < reportVariantsLength;) {
            if (ReportsVariants.get()[idx].variant == _variant) {
                return (int256(idx), ReportsVariants.get()[idx].votes);
            }
            unchecked {
                ++idx;
            }
        }
        return (-1, 0);
    }

    /// @notice Internal utility to retrieve a casted River interface
    /// @return The casted River interface
    function _river() internal view returns (IRiverV1) {
        return IRiverV1(payable(RiverAddress.get()));
    }
}

File 69 of 116 : IOracle.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./IRiver.1.sol";
import "../state/oracle/ReportsVariants.sol";

/// @title Oracle Interface (v1)
/// @author Kiln
/// @notice This interface exposes methods to handle the input from the allowed oracle members.
/// @notice Highly inspired by Lido's implementation.
interface IOracleV1 {
    /// @notice The storage quorum value has been changed
    /// @param newQuorum The new quorum value
    event SetQuorum(uint256 newQuorum);

    /// @notice A member has been added to the oracle member list
    /// @param member The address of the member
    event AddMember(address indexed member);

    /// @notice A member has been removed from the oracle member list
    /// @param member The address of the member
    event RemoveMember(address indexed member);

    /// @notice A member address has been edited
    /// @param oldAddress The previous member address
    /// @param newAddress The new member address
    event SetMember(address indexed oldAddress, address indexed newAddress);

    /// @notice The storage river address value has been changed
    /// @param _river The new river address
    event SetRiver(address _river);

    /// @notice The consensus layer spec has been changed
    /// @param epochsPerFrame The number of epochs inside a frame (225 = 24 hours)
    /// @param slotsPerEpoch The number of slots inside an epoch (32 on ethereum mainnet)
    /// @param secondsPerSlot The time between two slots (12 seconds on ethereum mainnet)
    /// @param genesisTime The timestamp of block #0
    event SetSpec(uint64 epochsPerFrame, uint64 slotsPerEpoch, uint64 secondsPerSlot, uint64 genesisTime);

    /// @notice The report bounds have been changed
    /// @param annualAprUpperBound The maximum allowed apr. 10% means increases in balance extrapolated to a year should not exceed 10%.
    /// @param relativeLowerBound The maximum allowed balance decrease as a relative % of the total balance
    event SetBounds(uint256 annualAprUpperBound, uint256 relativeLowerBound);

    /// @notice An oracle member performed a report
    /// @param member The oracle member
    /// @param variant The variant of the report
    /// @param report The raw report structure
    /// @param voteCount The vote count
    event ReportedConsensusLayerData(
        address indexed member,
        bytes32 indexed variant,
        IRiverV1.ConsensusLayerReport report,
        uint256 voteCount,
        uint256 quorum
    );

    /// @notice The last reported epoch has changed
    event SetLastReportedEpoch(uint256 lastReportedEpoch);

    /// @notice Cleared reporting data
    event ClearedReporting();

    /// @notice The provided epoch is too old compared to the expected epoch id
    /// @param providedEpochId The epoch id provided as input
    /// @param minExpectedEpochId The minimum epoch id expected
    error EpochTooOld(uint256 providedEpochId, uint256 minExpectedEpochId);

    /// @notice Thrown when the reported epoch is invalid
    /// @param epoch The invalid epoch
    error InvalidEpoch(uint256 epoch);

    /// @notice Thrown when the report indexs fetched is out of bounds
    /// @param index Requested index
    /// @param length Size of the variant array
    error ReportIndexOutOfBounds(uint256 index, uint256 length);

    /// @notice The member already reported on the given epoch id
    /// @param epochId The epoch id provided as input
    /// @param member The oracle member
    error AlreadyReported(uint256 epochId, address member);

    /// @notice The address is already in use by an oracle member
    /// @param newAddress The address already in use
    error AddressAlreadyInUse(address newAddress);

    /// @notice Initializes the oracle
    /// @param _river Address of the River contract, able to receive oracle input data after quorum is met
    /// @param _administratorAddress Address able to call administrative methods
    /// @param _epochsPerFrame CL spec parameter. Number of epochs in a frame.
    /// @param _slotsPerEpoch CL spec parameter. Number of slots in one epoch.
    /// @param _secondsPerSlot CL spec parameter. Number of seconds between slots.
    /// @param _genesisTime CL spec parameter. Timestamp of the genesis slot.
    /// @param _annualAprUpperBound CL bound parameter. Maximum apr allowed for balance increase. Delta between updates is extrapolated on a year time frame.
    /// @param _relativeLowerBound CL bound parameter. Maximum relative balance decrease.
    function initOracleV1(
        address _river,
        address _administratorAddress,
        uint64 _epochsPerFrame,
        uint64 _slotsPerEpoch,
        uint64 _secondsPerSlot,
        uint64 _genesisTime,
        uint256 _annualAprUpperBound,
        uint256 _relativeLowerBound
    ) external;

    /// @notice Initializes the oracle
    function initOracleV1_1() external;

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

    /// @notice Retrieve member report status
    /// @param _oracleMember Address of member to check
    /// @return True if member has reported
    function getMemberReportStatus(address _oracleMember) external view returns (bool);

    /// @notice Retrieve member report status
    /// @return The raw report status value
    function getGlobalReportStatus() external view returns (uint256);

    /// @notice Retrieve report variants count
    /// @return The count of report variants
    function getReportVariantsCount() external view returns (uint256);

    /// @notice Retrieve the details of a report variant
    /// @param _idx The index of the report variant
    /// @return The report variant details
    function getReportVariantDetails(uint256 _idx)
        external
        view
        returns (ReportsVariants.ReportVariantDetails memory);

    /// @notice Retrieve the current quorum
    /// @return The current quorum
    function getQuorum() external view returns (uint256);

    /// @notice Retrieve the list of oracle members
    /// @return The oracle members
    function getOracleMembers() external view returns (address[] memory);

    /// @notice Returns true if address is member
    /// @dev Performs a naive search, do not call this on-chain, used as an off-chain helper
    /// @param _memberAddress Address of the member
    /// @return True if address is a member
    function isMember(address _memberAddress) external view returns (bool);

    /// @notice Retrieve the last reported epoch id
    /// @dev The Oracle contracts expects reports on an epoch id >= that the returned value
    /// @return The last reported epoch id
    function getLastReportedEpochId() external view returns (uint256);

    /// @notice Adds new address as oracle member, giving the ability to push cl reports.
    /// @dev Only callable by the adminstrator
    /// @dev Modifying the quorum clears all the reporting data
    /// @param _newOracleMember Address of the new member
    /// @param _newQuorum New quorum value
    function addMember(address _newOracleMember, uint256 _newQuorum) external;

    /// @notice Removes an address from the oracle members.
    /// @dev Only callable by the adminstrator
    /// @dev Modifying the quorum clears all the reporting data
    /// @dev Remaining members that have already voted should vote again for the same frame.
    /// @param _oracleMember Address to remove
    /// @param _newQuorum New quorum value
    function removeMember(address _oracleMember, uint256 _newQuorum) external;

    /// @notice Changes the address of an oracle member
    /// @dev Only callable by the adminitrator or the member itself
    /// @dev Cannot use an address already in use
    /// @param _oracleMember Address to change
    /// @param _newAddress New address for the member
    function setMember(address _oracleMember, address _newAddress) external;

    /// @notice Edits the quorum required to forward cl data to River
    /// @dev Modifying the quorum clears all the reporting data
    /// @param _newQuorum New quorum parameter
    function setQuorum(uint256 _newQuorum) external;

    /// @notice Submit a report as an oracle member
    /// @param _report The report structure
    function reportConsensusLayerData(IRiverV1.ConsensusLayerReport calldata _report) external;
}

File 70 of 116 : LastEpochId.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Last Epoch Id Storage
/// @notice Utility to manage the Last Epoch Id in storage
library LastEpochId {
    /// @notice Storage slot of the Last Epoch Id
    bytes32 internal constant LAST_EPOCH_ID_SLOT = bytes32(uint256(keccak256("river.state.lastEpochId")) - 1);

    /// @notice Retrieve the Last Epoch Id
    /// @return The Last Epoch Id
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(LAST_EPOCH_ID_SLOT);
    }

    /// @notice Sets the Last Epoch Id
    /// @param _newValue New Last Epoch Id
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(LAST_EPOCH_ID_SLOT, _newValue);
    }
}

File 71 of 116 : Quorum.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Quorum Storage
/// @notice Utility to manage the Quorum in storage
library Quorum {
    /// @notice Storage slot of the Quorum
    bytes32 internal constant QUORUM_SLOT = bytes32(uint256(keccak256("river.state.quorum")) - 1);

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

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

File 72 of 116 : ReportsPositions.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Reports Positions Storage
/// @notice Utility to manage the Reports Positions in storage
/// @dev Each bit in the stored uint256 value tells if the member at a given index has reported
library ReportsPositions {
    /// @notice Storage slot of the Reports Positions
    bytes32 internal constant REPORTS_POSITIONS_SLOT = bytes32(uint256(keccak256("river.state.reportsPositions")) - 1);

    /// @notice Retrieve the Reports Positions at index
    /// @param _idx The index to retrieve
    /// @return True if already reported
    function get(uint256 _idx) internal view returns (bool) {
        uint256 mask = 1 << _idx;
        return LibUnstructuredStorage.getStorageUint256(REPORTS_POSITIONS_SLOT) & mask == mask;
    }

    /// @notice Retrieve the raw Reports Positions from storage
    /// @return Raw Reports Positions
    function getRaw() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(REPORTS_POSITIONS_SLOT);
    }

    /// @notice Register an index as reported
    /// @param _idx The index to register
    function register(uint256 _idx) internal {
        uint256 mask = 1 << _idx;
        return LibUnstructuredStorage.setStorageUint256(
            REPORTS_POSITIONS_SLOT, LibUnstructuredStorage.getStorageUint256(REPORTS_POSITIONS_SLOT) | mask
        );
    }

    /// @notice Clears all the report positions in storage
    function clear() internal {
        return LibUnstructuredStorage.setStorageUint256(REPORTS_POSITIONS_SLOT, 0);
    }
}

File 73 of 116 : ReportsVariants.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Reports Variants Storage
/// @notice Utility to manage the Reports Variants in storage
library ReportsVariants {
    /// @notice Storage slot of the Reports Variants
    bytes32 internal constant REPORT_VARIANTS_SLOT = bytes32(uint256(keccak256("river.state.reportsVariants")) - 1);

    struct ReportVariantDetails {
        bytes32 variant;
        uint256 votes;
    }

    /// @notice Structure in storage
    struct Slot {
        /// @custom:attribute The list of variants
        ReportVariantDetails[] value;
    }

    /// @notice Retrieve the Reports Variants from storage
    /// @return The Reports Variants
    function get() internal view returns (ReportVariantDetails[] storage) {
        bytes32 slot = REPORT_VARIANTS_SLOT;

        Slot storage r;

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

        return r.value;
    }

    /// @notice Set the Reports Variants value at index
    /// @param _idx The index to set
    /// @param _val The value to set
    function set(uint256 _idx, ReportVariantDetails memory _val) internal {
        bytes32 slot = REPORT_VARIANTS_SLOT;

        Slot storage r;

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

        r.value[_idx] = _val;
    }

    /// @notice Add a new variant in the list
    /// @param _variant The new variant to add
    function push(ReportVariantDetails memory _variant) internal {
        bytes32 slot = REPORT_VARIANTS_SLOT;

        Slot storage r;

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

        r.value.push(_variant);
    }

    /// @notice Retrieve the index of a specific variant, ignoring the count field
    /// @param _variant Variant value to lookup
    /// @return The index of the variant, -1 if not found
    function indexOfReport(bytes32 _variant) internal view returns (int256) {
        bytes32 slot = REPORT_VARIANTS_SLOT;

        Slot storage r;

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

        for (uint256 idx = 0; idx < r.value.length;) {
            if (r.value[idx].variant == _variant) {
                return int256(idx);
            }
            unchecked {
                ++idx;
            }
        }

        return int256(-1);
    }

    /// @notice Clear all variants from storage
    function clear() internal {
        bytes32 slot = REPORT_VARIANTS_SLOT;

        Slot storage r;

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

        delete r.value;
    }
}

File 74 of 116 : Withdraw.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./Initializable.sol";
import "./interfaces/IRiver.1.sol";
import "./interfaces/IWithdraw.1.sol";
import "./libraries/LibErrors.sol";
import "./libraries/LibUint256.sol";

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

/// @title Withdraw (v1)
/// @author Kiln
/// @notice This contract is in charge of holding the exit and skimming funds and allow river to pull these funds
contract WithdrawV1 is IWithdrawV1, Initializable {
    modifier onlyRiver() {
        if (msg.sender != RiverAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @inheritdoc IWithdrawV1
    function initializeWithdrawV1(address _river) external init(0) {
        _setRiver(_river);
    }

    /// @inheritdoc IWithdrawV1
    function getCredentials() external view returns (bytes32) {
        return bytes32(
            uint256(uint160(address(this))) + 0x0100000000000000000000000000000000000000000000000000000000000000
        );
    }

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

    /// @inheritdoc IWithdrawV1
    function pullEth(uint256 _max) external onlyRiver {
        uint256 amountToPull = LibUint256.min(address(this).balance, _max);
        if (amountToPull > 0) {
            IRiverV1(payable(RiverAddress.get())).sendCLFunds{value: amountToPull}();
        }
    }

    /// @notice Internal utility to set the river address
    /// @param _river The new river address
    function _setRiver(address _river) internal {
        RiverAddress.set(_river);
        emit SetRiver(_river);
    }
}

File 75 of 116 : 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 76 of 116 : 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 77 of 116 : ValidatorKeys.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../libraries/LibBytes.sol";

/// @title Validator Keys Storage
/// @notice Utility to manage the validator keys in storage
library ValidatorKeys {
    /// @notice Storage slot of the Validator Keys
    bytes32 internal constant VALIDATOR_KEYS_SLOT = bytes32(uint256(keccak256("river.state.validatorKeys")) - 1);

    /// @notice Length in bytes of a BLS Public Key used for validator deposits
    uint256 internal constant PUBLIC_KEY_LENGTH = 48;

    /// @notice Length in bytes of a BLS Signature used for validator deposits
    uint256 internal constant SIGNATURE_LENGTH = 96;

    /// @notice The provided public key is not matching the expected length
    error InvalidPublicKey();

    /// @notice The provided signature is not matching the expected length
    error InvalidSignature();

    /// @notice Structure of the Validator Keys in storage
    struct Slot {
        /// @custom:attribute The mapping from operator index to key index to key value
        mapping(uint256 => mapping(uint256 => bytes)) value;
    }

    /// @notice Retrieve the Validator Key of an operator at a specific index
    /// @param _operatorIndex The operator index
    /// @param _idx the Validator Key index
    /// @return publicKey The Validator Key public key
    /// @return signature The Validator Key signature
    function get(uint256 _operatorIndex, uint256 _idx)
        internal
        view
        returns (bytes memory publicKey, bytes memory signature)
    {
        bytes32 slot = VALIDATOR_KEYS_SLOT;

        Slot storage r;

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

        bytes storage entry = r.value[_operatorIndex][_idx];

        publicKey = LibBytes.slice(entry, 0, PUBLIC_KEY_LENGTH);
        signature = LibBytes.slice(entry, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH);
    }

    /// @notice Retrieve the raw concatenated Validator Keys
    /// @param _operatorIndex The operator index
    /// @param _idx The Validator Key index
    /// @return The concatenated public key and signature
    function getRaw(uint256 _operatorIndex, uint256 _idx) internal view returns (bytes memory) {
        bytes32 slot = VALIDATOR_KEYS_SLOT;

        Slot storage r;

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

        return r.value[_operatorIndex][_idx];
    }

    /// @notice Retrieve multiple keys of an operator starting at an index
    /// @param _operatorIndex The operator index
    /// @param _startIdx The starting index to retrieve the keys from
    /// @param _amount The amount of keys to retrieve
    /// @return publicKeys The public keys retrieved
    /// @return signatures The signatures associated with the public keys
    function getKeys(uint256 _operatorIndex, uint256 _startIdx, uint256 _amount)
        internal
        view
        returns (bytes[] memory publicKeys, bytes[] memory signatures)
    {
        publicKeys = new bytes[](_amount);
        signatures = new bytes[](_amount);

        bytes32 slot = VALIDATOR_KEYS_SLOT;

        Slot storage r;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            r.slot := slot
        }
        uint256 idx;
        for (; idx < _amount;) {
            bytes memory rawCredentials = r.value[_operatorIndex][idx + _startIdx];
            publicKeys[idx] = LibBytes.slice(rawCredentials, 0, PUBLIC_KEY_LENGTH);
            signatures[idx] = LibBytes.slice(rawCredentials, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH);
            unchecked {
                ++idx;
            }
        }
    }

    /// @notice Set the concatenated Validator Keys at an index for an operator
    /// @param _operatorIndex The operator index
    /// @param _idx The key index to write on
    /// @param _publicKeyAndSignature The concatenated Validator Keys
    function set(uint256 _operatorIndex, uint256 _idx, bytes memory _publicKeyAndSignature) internal {
        bytes32 slot = VALIDATOR_KEYS_SLOT;

        Slot storage r;

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

        r.value[_operatorIndex][_idx] = _publicKeyAndSignature;
    }
}

File 78 of 116 : OperatorsRegistry.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IOperatorRegistry.1.sol";
import "./interfaces/IRiver.1.sol";

import "./libraries/LibUint256.sol";

import "./Initializable.sol";
import "./Administrable.sol";

import "./state/operatorsRegistry/Operators.1.sol";
import "./state/operatorsRegistry/Operators.2.sol";
import "./state/operatorsRegistry/ValidatorKeys.sol";
import "./state/operatorsRegistry/TotalValidatorExitsRequested.sol";
import "./state/operatorsRegistry/CurrentValidatorExitsDemand.sol";
import "./state/shared/RiverAddress.sol";

import "./state/migration/OperatorsRegistry_FundedKeyEventRebroadcasting_KeyIndex.sol";
import "./state/migration/OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex.sol";

/// @title Operators Registry (v1)
/// @author Kiln
/// @notice This contract handles the list of operators and their keys
contract OperatorsRegistryV1 is IOperatorsRegistryV1, Initializable, Administrable {
    /// @notice Maximum validators given to an operator per selection loop round
    uint256 internal constant MAX_VALIDATOR_ATTRIBUTION_PER_ROUND = 5;

    /// @inheritdoc IOperatorsRegistryV1
    function initOperatorsRegistryV1(address _admin, address _river) external init(0) {
        _setAdmin(_admin);
        RiverAddress.set(_river);
        emit SetRiver(_river);
    }

    /// @notice Internal migration utility to migrate all operators to OperatorsV2 format
    function _migrateOperators_V1_1() internal {
        uint256 opCount = OperatorsV1.getCount();

        for (uint256 idx = 0; idx < opCount;) {
            OperatorsV1.Operator memory oldOperatorValue = OperatorsV1.get(idx);

            OperatorsV2.push(
                OperatorsV2.Operator({
                    limit: uint32(oldOperatorValue.limit),
                    funded: uint32(oldOperatorValue.funded),
                    requestedExits: 0,
                    keys: uint32(oldOperatorValue.keys),
                    latestKeysEditBlockNumber: uint64(oldOperatorValue.latestKeysEditBlockNumber),
                    active: oldOperatorValue.active,
                    name: oldOperatorValue.name,
                    operator: oldOperatorValue.operator
                })
            );

            unchecked {
                ++idx;
            }
        }
    }

    /// MIGRATION: FUNDED VALIDATOR KEY EVENT REBROADCASTING
    /// As the event for funded keys was moved from River to this contract because we needed to be able to bind
    /// operator indexes to public keys, we need to rebroadcast the past funded validator keys with the new event
    /// to keep retro-compatibility

    /// Emitted when the event rebroadcasting is done and we attempt to broadcast new events
    error FundedKeyEventMigrationComplete();

    /// Utility to force the broadcasting of events. Will keep its progress in storage to prevent being DoSed by the number of keys
    /// @param _amountToEmit The amount of events to emit at maximum in this call
    function forceFundedValidatorKeysEventEmission(uint256 _amountToEmit) external {
        uint256 operatorIndex = OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex.get();
        if (operatorIndex == type(uint256).max) {
            revert FundedKeyEventMigrationComplete();
        }
        if (OperatorsV2.getCount() == 0) {
            OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex.set(type(uint256).max);
            return;
        }
        uint256 keyIndex = OperatorsRegistry_FundedKeyEventRebroadcasting_KeyIndex.get();
        while (_amountToEmit > 0 && operatorIndex != type(uint256).max) {
            OperatorsV2.Operator memory operator = OperatorsV2.get(operatorIndex);

            (bytes[] memory publicKeys,) = ValidatorKeys.getKeys(
                operatorIndex, keyIndex, LibUint256.min(_amountToEmit, operator.funded - keyIndex)
            );
            emit FundedValidatorKeys(operatorIndex, publicKeys, true);
            if (keyIndex + publicKeys.length == operator.funded) {
                keyIndex = 0;
                if (operatorIndex == OperatorsV2.getCount() - 1) {
                    operatorIndex = type(uint256).max;
                } else {
                    ++operatorIndex;
                }
            } else {
                keyIndex += publicKeys.length;
            }
            _amountToEmit -= publicKeys.length;
        }
        OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex.set(operatorIndex);
        OperatorsRegistry_FundedKeyEventRebroadcasting_KeyIndex.set(keyIndex);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function initOperatorsRegistryV1_1() external init(1) {
        _migrateOperators_V1_1();
    }

    /// @notice Prevent unauthorized calls
    modifier onlyRiver() virtual {
        if (msg.sender != RiverAddress.get()) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @notice Prevents anyone except the admin or the given operator to make the call. Also checks if operator is active
    /// @notice The admin is able to call this method on behalf of any operator, even if inactive
    /// @param _index The index identifying the operator
    modifier onlyOperatorOrAdmin(uint256 _index) {
        if (msg.sender == _getAdmin()) {
            _;
            return;
        }
        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);
        if (!operator.active) {
            revert InactiveOperator(_index);
        }
        if (msg.sender != operator.operator) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

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

    /// @inheritdoc IOperatorsRegistryV1
    function getOperator(uint256 _index) external view returns (OperatorsV2.Operator memory) {
        return OperatorsV2.get(_index);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getOperatorStoppedValidatorCount(uint256 _idx) external view returns (uint32) {
        return _getStoppedValidatorsCount(_idx);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getTotalStoppedValidatorCount() external view returns (uint32) {
        return _getTotalStoppedValidatorCount();
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getTotalValidatorExitsRequested() external view returns (uint256) {
        return TotalValidatorExitsRequested.get();
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getCurrentValidatorExitsDemand() external view returns (uint256) {
        return CurrentValidatorExitsDemand.get();
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getStoppedAndRequestedExitCounts() external view returns (uint32, uint256) {
        return
            (_getTotalStoppedValidatorCount(), TotalValidatorExitsRequested.get() + CurrentValidatorExitsDemand.get());
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getOperatorCount() external view returns (uint256) {
        return OperatorsV2.getCount();
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getStoppedValidatorCountPerOperator() external view returns (uint32[] memory) {
        uint32[] memory completeList = OperatorsV2.getStoppedValidators();
        uint256 listLength = completeList.length;

        if (listLength > 0) {
            assembly {
                // no need to use free memory pointer as we reuse the same memory range

                // erase previous word storing length
                mstore(completeList, 0)

                // move memory pointer up by a word
                completeList := add(completeList, 0x20)

                // store updated length at new memory pointer location
                mstore(completeList, sub(listLength, 1))
            }
        }

        return completeList;
    }

    /// @inheritdoc IOperatorsRegistryV1
    function getValidator(uint256 _operatorIndex, uint256 _validatorIndex)
        external
        view
        returns (bytes memory publicKey, bytes memory signature, bool funded)
    {
        (publicKey, signature) = ValidatorKeys.get(_operatorIndex, _validatorIndex);
        funded = _validatorIndex < OperatorsV2.get(_operatorIndex).funded;
    }

    /// @inheritdoc IOperatorsRegistryV1
    function listActiveOperators() external view returns (OperatorsV2.Operator[] memory) {
        return OperatorsV2.getAllActive();
    }

    /// @inheritdoc IOperatorsRegistryV1
    function reportStoppedValidatorCounts(uint32[] calldata _stoppedValidatorCounts, uint256 _depositedValidatorCount)
        external
        onlyRiver
    {
        _setStoppedValidatorCounts(_stoppedValidatorCounts, _depositedValidatorCount);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function addOperator(string calldata _name, address _operator) external onlyAdmin returns (uint256) {
        OperatorsV2.Operator memory newOperator = OperatorsV2.Operator({
            active: true,
            operator: _operator,
            name: _name,
            limit: 0,
            funded: 0,
            keys: 0,
            requestedExits: 0,
            latestKeysEditBlockNumber: uint64(block.number)
        });

        uint256 operatorIndex = OperatorsV2.push(newOperator) - 1;

        emit AddedOperator(operatorIndex, _name, _operator);
        return operatorIndex;
    }

    /// @inheritdoc IOperatorsRegistryV1
    function setOperatorAddress(uint256 _index, address _newOperatorAddress) external onlyOperatorOrAdmin(_index) {
        LibSanitize._notZeroAddress(_newOperatorAddress);
        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);

        operator.operator = _newOperatorAddress;

        emit SetOperatorAddress(_index, _newOperatorAddress);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function setOperatorName(uint256 _index, string calldata _newName) external onlyOperatorOrAdmin(_index) {
        LibSanitize._notEmptyString(_newName);
        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);
        operator.name = _newName;

        emit SetOperatorName(_index, _newName);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function setOperatorStatus(uint256 _index, bool _newStatus) external onlyAdmin {
        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);
        operator.active = _newStatus;

        emit SetOperatorStatus(_index, _newStatus);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function setOperatorLimits(
        uint256[] calldata _operatorIndexes,
        uint32[] calldata _newLimits,
        uint256 _snapshotBlock
    ) external onlyAdmin {
        if (_operatorIndexes.length != _newLimits.length) {
            revert InvalidArrayLengths();
        }
        if (_operatorIndexes.length == 0) {
            revert InvalidEmptyArray();
        }
        for (uint256 idx = 0; idx < _operatorIndexes.length;) {
            uint256 operatorIndex = _operatorIndexes[idx];
            uint32 newLimit = _newLimits[idx];

            // prevents duplicates
            if (idx > 0 && !(operatorIndex > _operatorIndexes[idx - 1])) {
                revert UnorderedOperatorList();
            }

            OperatorsV2.Operator storage operator = OperatorsV2.get(operatorIndex);

            uint32 currentLimit = operator.limit;
            if (newLimit == currentLimit) {
                emit OperatorLimitUnchanged(operatorIndex, newLimit);
                unchecked {
                    ++idx;
                }
                continue;
            }

            // we enter this condition if the operator edited its keys after the off-chain key audit was made
            // we will skip any limit update on that operator unless it was a decrease in the initial limit
            if (_snapshotBlock < operator.latestKeysEditBlockNumber && newLimit > currentLimit) {
                emit OperatorEditsAfterSnapshot(
                    operatorIndex, currentLimit, newLimit, operator.latestKeysEditBlockNumber, _snapshotBlock
                );
                unchecked {
                    ++idx;
                }
                continue;
            }

            // otherwise, we check for limit invariants that shouldn't happen if the off-chain key audit
            // was made properly, and if everything is respected, we update the limit

            if (newLimit > operator.keys) {
                revert OperatorLimitTooHigh(operatorIndex, newLimit, operator.keys);
            }

            if (newLimit < operator.funded) {
                revert OperatorLimitTooLow(operatorIndex, newLimit, operator.funded);
            }

            operator.limit = newLimit;
            emit SetOperatorLimit(operatorIndex, newLimit);

            unchecked {
                ++idx;
            }
        }
    }

    /// @inheritdoc IOperatorsRegistryV1
    function addValidators(uint256 _index, uint32 _keyCount, bytes calldata _publicKeysAndSignatures)
        external
        onlyOperatorOrAdmin(_index)
    {
        if (_keyCount == 0) {
            revert InvalidKeyCount();
        }

        if (
            _publicKeysAndSignatures.length
                != _keyCount * (ValidatorKeys.PUBLIC_KEY_LENGTH + ValidatorKeys.SIGNATURE_LENGTH)
        ) {
            revert InvalidKeysLength();
        }

        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);

        for (uint256 idx = 0; idx < _keyCount;) {
            bytes memory publicKeyAndSignature = LibBytes.slice(
                _publicKeysAndSignatures,
                idx * (ValidatorKeys.PUBLIC_KEY_LENGTH + ValidatorKeys.SIGNATURE_LENGTH),
                ValidatorKeys.PUBLIC_KEY_LENGTH + ValidatorKeys.SIGNATURE_LENGTH
            );
            ValidatorKeys.set(_index, operator.keys + idx, publicKeyAndSignature);
            unchecked {
                ++idx;
            }
        }
        OperatorsV2.setKeys(_index, operator.keys + _keyCount);

        emit AddedValidatorKeys(_index, _publicKeysAndSignatures);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function removeValidators(uint256 _index, uint256[] calldata _indexes) external onlyOperatorOrAdmin(_index) {
        uint256 indexesLength = _indexes.length;
        if (indexesLength == 0) {
            revert InvalidKeyCount();
        }

        OperatorsV2.Operator storage operator = OperatorsV2.get(_index);

        uint32 totalKeys = operator.keys;

        if (!(_indexes[0] < totalKeys)) {
            revert InvalidIndexOutOfBounds();
        }

        uint256 lastIndex = _indexes[indexesLength - 1];

        if (lastIndex < operator.funded) {
            revert InvalidFundedKeyDeletionAttempt();
        }

        bool limitEqualsKeyCount = operator.keys == operator.limit;
        OperatorsV2.setKeys(_index, totalKeys - uint32(indexesLength));

        uint256 idx;
        for (; idx < indexesLength;) {
            uint256 keyIndex = _indexes[idx];

            if (idx > 0 && !(keyIndex < _indexes[idx - 1])) {
                revert InvalidUnsortedIndexes();
            }

            unchecked {
                ++idx;
            }

            uint256 lastKeyIndex = totalKeys - idx;

            (bytes memory removedPublicKey,) = ValidatorKeys.get(_index, keyIndex);
            (bytes memory lastPublicKeyAndSignature) = ValidatorKeys.getRaw(_index, lastKeyIndex);
            ValidatorKeys.set(_index, keyIndex, lastPublicKeyAndSignature);
            ValidatorKeys.set(_index, lastKeyIndex, new bytes(0));

            emit RemovedValidatorKey(_index, removedPublicKey);
        }

        if (limitEqualsKeyCount) {
            operator.limit = operator.keys;
        } else if (lastIndex < operator.limit) {
            operator.limit = uint32(lastIndex);
        }
    }

    /// @inheritdoc IOperatorsRegistryV1
    function pickNextValidatorsToDeposit(uint256 _count)
        external
        onlyRiver
        returns (bytes[] memory publicKeys, bytes[] memory signatures)
    {
        return _pickNextValidatorsToDepositFromActiveOperators(_count);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function requestValidatorExits(uint256 _count) external {
        uint256 currentValidatorExitsDemand = CurrentValidatorExitsDemand.get();
        uint256 exitRequestsToPerform = LibUint256.min(currentValidatorExitsDemand, _count);
        if (exitRequestsToPerform == 0) {
            revert NoExitRequestsToPerform();
        }
        uint256 savedCurrentValidatorExitsDemand = currentValidatorExitsDemand;
        currentValidatorExitsDemand -= _pickNextValidatorsToExitFromActiveOperators(exitRequestsToPerform);

        _setCurrentValidatorExitsDemand(savedCurrentValidatorExitsDemand, currentValidatorExitsDemand);
    }

    /// @inheritdoc IOperatorsRegistryV1
    function demandValidatorExits(uint256 _count, uint256 _depositedValidatorCount) external onlyRiver {
        uint256 currentValidatorExitsDemand = CurrentValidatorExitsDemand.get();
        uint256 totalValidatorExitsRequested = TotalValidatorExitsRequested.get();
        _count = LibUint256.min(
            _count, _depositedValidatorCount - (totalValidatorExitsRequested + currentValidatorExitsDemand)
        );
        if (_count > 0) {
            _setCurrentValidatorExitsDemand(currentValidatorExitsDemand, currentValidatorExitsDemand + _count);
        }
    }

    /// @notice Internal utility to retrieve the total stopped validator count
    /// @return The total stopped validator count
    function _getTotalStoppedValidatorCount() internal view returns (uint32) {
        uint32[] storage stoppedValidatorCounts = OperatorsV2.getStoppedValidators();
        if (stoppedValidatorCounts.length == 0) {
            return 0;
        }
        return stoppedValidatorCounts[0];
    }

    /// @notice Internal utility to set the current validator exits demand
    /// @param _currentValue The current value
    /// @param _newValue The new value
    function _setCurrentValidatorExitsDemand(uint256 _currentValue, uint256 _newValue) internal {
        CurrentValidatorExitsDemand.set(_newValue);
        emit SetCurrentValidatorExitsDemand(_currentValue, _newValue);
    }

    /// @notice Internal structure to hold variables for the _setStoppedValidatorCounts method
    struct SetStoppedValidatorCountInternalVars {
        uint256 stoppedValidatorCountsLength;
        uint32[] currentStoppedValidatorCounts;
        uint256 currentStoppedValidatorCountsLength;
        uint32 totalStoppedValidatorCount;
        uint32 count;
        uint256 currentValidatorExitsDemand;
        uint256 cachedCurrentValidatorExitsDemand;
        uint256 totalRequestedExits;
        uint256 cachedTotalRequestedExits;
    }

    /// @notice Internal utiltiy to set the stopped validator array after sanity checks
    /// @param _stoppedValidatorCounts The stopped validators counts for every operator + the total count in index 0
    /// @param _depositedValidatorCount The current deposited validator count
    function _setStoppedValidatorCounts(uint32[] calldata _stoppedValidatorCounts, uint256 _depositedValidatorCount)
        internal
    {
        SetStoppedValidatorCountInternalVars memory vars;
        // we check that the array is not empty
        vars.stoppedValidatorCountsLength = _stoppedValidatorCounts.length;
        if (vars.stoppedValidatorCountsLength == 0) {
            revert InvalidEmptyStoppedValidatorCountsArray();
        }

        OperatorsV2.Operator[] storage operators = OperatorsV2.getAll();

        // we check that the cells containing operator stopped values are no more than the current operator count
        if (vars.stoppedValidatorCountsLength - 1 > operators.length) {
            revert StoppedValidatorCountsTooHigh();
        }

        vars.currentStoppedValidatorCounts = OperatorsV2.getStoppedValidators();
        vars.currentStoppedValidatorCountsLength = vars.currentStoppedValidatorCounts.length;

        // we check that the number of stopped values is not decreasing
        if (vars.stoppedValidatorCountsLength < vars.currentStoppedValidatorCountsLength) {
            revert StoppedValidatorCountArrayShrinking();
        }

        vars.totalStoppedValidatorCount = _stoppedValidatorCounts[0];
        vars.count = 0;

        // create value to track unsollicited validator exits (e.g. to cover cases when Node Operator exit a validator without being requested to)
        vars.currentValidatorExitsDemand = CurrentValidatorExitsDemand.get();
        vars.cachedCurrentValidatorExitsDemand = vars.currentValidatorExitsDemand;
        vars.totalRequestedExits = TotalValidatorExitsRequested.get();
        vars.cachedTotalRequestedExits = vars.totalRequestedExits;

        uint256 idx = 1;
        uint256 unsollicitedExitsSum;
        for (; idx < vars.currentStoppedValidatorCountsLength;) {
            // if the previous array was long enough, we check that the values are not decreasing
            if (_stoppedValidatorCounts[idx] < vars.currentStoppedValidatorCounts[idx]) {
                revert StoppedValidatorCountsDecreased();
            }

            // we check that the count of stopped validators is not above the funded validator count of an operator
            if (_stoppedValidatorCounts[idx] > operators[idx - 1].funded) {
                revert StoppedValidatorCountAboveFundedCount(
                    idx - 1, _stoppedValidatorCounts[idx], operators[idx - 1].funded
                );
            }

            // if the stopped validator count is greater than its requested exit count, we update the requested exit count
            if (_stoppedValidatorCounts[idx] > operators[idx - 1].requestedExits) {
                emit UpdatedRequestedValidatorExitsUponStopped(
                    idx - 1, operators[idx - 1].requestedExits, _stoppedValidatorCounts[idx]
                );
                unsollicitedExitsSum += _stoppedValidatorCounts[idx] - operators[idx - 1].requestedExits;
                operators[idx - 1].requestedExits = _stoppedValidatorCounts[idx];
            }
            emit SetOperatorStoppedValidatorCount(idx - 1, _stoppedValidatorCounts[idx]);

            // we recompute the total to ensure it's not an invalid sum
            vars.count += _stoppedValidatorCounts[idx];
            unchecked {
                ++idx;
            }
        }

        // In case of a new operator we do not check against the current stopped validator count (would revert OOB)
        for (; idx < vars.stoppedValidatorCountsLength;) {
            // we check that the count of stopped validators is not above the funded validator count of an operator
            if (_stoppedValidatorCounts[idx] > operators[idx - 1].funded) {
                revert StoppedValidatorCountAboveFundedCount(
                    idx - 1, _stoppedValidatorCounts[idx], operators[idx - 1].funded
                );
            }

            // if the stopped validator count is greater than its requested exit count, we update the requested exit count
            if (_stoppedValidatorCounts[idx] > operators[idx - 1].requestedExits) {
                emit UpdatedRequestedValidatorExitsUponStopped(
                    idx - 1, operators[idx - 1].requestedExits, _stoppedValidatorCounts[idx]
                );
                unsollicitedExitsSum += _stoppedValidatorCounts[idx] - operators[idx - 1].requestedExits;
                operators[idx - 1].requestedExits = _stoppedValidatorCounts[idx];
            }
            emit SetOperatorStoppedValidatorCount(idx - 1, _stoppedValidatorCounts[idx]);

            // we recompute the total to ensure it's not an invalid sum
            vars.count += _stoppedValidatorCounts[idx];
            unchecked {
                ++idx;
            }
        }

        vars.totalRequestedExits += unsollicitedExitsSum;
        // we decrease the demand, considering unsollicited exits as if they were answering the demand
        vars.currentValidatorExitsDemand -= LibUint256.min(unsollicitedExitsSum, vars.currentValidatorExitsDemand);

        if (vars.totalRequestedExits != vars.cachedTotalRequestedExits) {
            _setTotalValidatorExitsRequested(vars.cachedTotalRequestedExits, vars.totalRequestedExits);
        }

        if (vars.currentValidatorExitsDemand != vars.cachedCurrentValidatorExitsDemand) {
            _setCurrentValidatorExitsDemand(vars.cachedCurrentValidatorExitsDemand, vars.currentValidatorExitsDemand);
        }

        // we check that the total is matching the sum of the individual values
        if (vars.totalStoppedValidatorCount != vars.count) {
            revert InvalidStoppedValidatorCountsSum();
        }
        // we check that the total is not higher than the current deposited validator count
        if (vars.totalStoppedValidatorCount > _depositedValidatorCount) {
            revert StoppedValidatorCountsTooHigh();
        }
        // we set the new stopped validators counts
        OperatorsV2.setRawStoppedValidators(_stoppedValidatorCounts);
        emit UpdatedStoppedValidators(_stoppedValidatorCounts);
    }

    /// @notice Internal utility to concatenate bytes arrays together
    /// @param _arr1 First array
    /// @param _arr2 Second array
    /// @return The result of the concatenation of _arr1 + _arr2
    function _concatenateByteArrays(bytes[] memory _arr1, bytes[] memory _arr2)
        internal
        pure
        returns (bytes[] memory)
    {
        bytes[] memory res = new bytes[](_arr1.length + _arr2.length);
        for (uint256 idx = 0; idx < _arr1.length;) {
            res[idx] = _arr1[idx];
            unchecked {
                ++idx;
            }
        }
        for (uint256 idx = 0; idx < _arr2.length;) {
            res[idx + _arr1.length] = _arr2[idx];
            unchecked {
                ++idx;
            }
        }
        return res;
    }

    /// @notice Internal utility to verify if an operator has fundable keys during the selection process
    /// @param _operator The Operator structure in memory
    /// @return True if at least one fundable key is available
    function _hasFundableKeys(OperatorsV2.CachedOperator memory _operator) internal pure returns (bool) {
        return (_operator.funded + _operator.picked) < _operator.limit;
    }

    /// @notice Internal utility to retrieve the actual stopped validator count of an operator from the reported array
    /// @param _operatorIndex The operator index
    /// @return The count of stopped validators
    function _getStoppedValidatorsCount(uint256 _operatorIndex) internal view returns (uint32) {
        return OperatorsV2._getStoppedValidatorCountAtIndex(OperatorsV2.getStoppedValidators(), _operatorIndex);
    }

    /// @notice Internal utility to get the count of active validators during the deposit selection process
    /// @param _operator The Operator structure in memory
    /// @return The count of active validators for the operator
    function _getActiveValidatorCountForDeposits(OperatorsV2.CachedOperator memory _operator)
        internal
        view
        returns (uint256)
    {
        return (_operator.funded + _operator.picked) - _getStoppedValidatorsCount(_operator.index);
    }

    /// @notice Internal utility to retrieve _count or lower fundable keys
    /// @dev The selection process starts by retrieving the full list of active operators with at least one fundable key.
    /// @dev
    /// @dev An operator is considered to have at least one fundable key when their staking limit is higher than their funded key count.
    /// @dev
    /// @dev    isFundable = operator.active && operator.limit > operator.funded
    /// @dev
    /// @dev The internal utility will loop on all operators and select the operator with the lowest active validator count.
    /// @dev The active validator count is computed by subtracting the stopped validator count to the funded validator count.
    /// @dev
    /// @dev    activeValidatorCount = operator.funded - operator.stopped
    /// @dev
    /// @dev During the selection process, we keep in memory all previously selected operators and the number of given validators inside a field
    /// @dev called picked that only exists on the CachedOperator structure in memory.
    /// @dev
    /// @dev    isFundable = operator.active && operator.limit > (operator.funded + operator.picked)
    /// @dev    activeValidatorCount = (operator.funded + operator.picked) - operator.stopped
    /// @dev
    /// @dev When we reach the requested key count or when all available keys are used, we perform a final loop on all the operators and extract keys
    /// @dev if any operator has a positive picked count. We then update the storage counters and return the arrays with the public keys and signatures.
    /// @param _count Amount of keys required. Contract is expected to send _count or lower.
    /// @return publicKeys An array of fundable public keys
    /// @return signatures An array of signatures linked to the public keys
    function _pickNextValidatorsToDepositFromActiveOperators(uint256 _count)
        internal
        returns (bytes[] memory publicKeys, bytes[] memory signatures)
    {
        (OperatorsV2.CachedOperator[] memory operators, uint256 fundableOperatorCount) = OperatorsV2.getAllFundable();

        if (fundableOperatorCount == 0) {
            return (new bytes[](0), new bytes[](0));
        }

        while (_count > 0) {
            // loop on operators to find the first that has fundable keys, taking into account previous loop round attributions
            uint256 selectedOperatorIndex = 0;
            for (; selectedOperatorIndex < fundableOperatorCount;) {
                if (_hasFundableKeys(operators[selectedOperatorIndex])) {
                    break;
                }
                unchecked {
                    ++selectedOperatorIndex;
                }
            }

            // if we reach the end, we have allocated all keys
            if (selectedOperatorIndex == fundableOperatorCount) {
                break;
            }

            // we start from the next operator and we try to find one that has fundable keys but a lower (funded + picked) - stopped value
            for (uint256 idx = selectedOperatorIndex + 1; idx < fundableOperatorCount;) {
                if (
                    _getActiveValidatorCountForDeposits(operators[idx])
                        < _getActiveValidatorCountForDeposits(operators[selectedOperatorIndex])
                        && _hasFundableKeys(operators[idx])
                ) {
                    selectedOperatorIndex = idx;
                }
                unchecked {
                    ++idx;
                }
            }

            // we take the smallest value between limit - (funded + picked), _requestedAmount and MAX_VALIDATOR_ATTRIBUTION_PER_ROUND
            uint256 pickedKeyCount = LibUint256.min(
                LibUint256.min(
                    operators[selectedOperatorIndex].limit
                        - (operators[selectedOperatorIndex].funded + operators[selectedOperatorIndex].picked),
                    MAX_VALIDATOR_ATTRIBUTION_PER_ROUND
                ),
                _count
            );

            // we update the cached picked amount
            operators[selectedOperatorIndex].picked += uint32(pickedKeyCount);

            // we update the requested amount count
            _count -= pickedKeyCount;
        }

        // we loop on all operators
        for (uint256 idx = 0; idx < fundableOperatorCount; ++idx) {
            // if we picked keys on any operator, we extract the keys from storage and concatenate them in the result
            // we then update the funded value
            if (operators[idx].picked > 0) {
                (bytes[] memory _publicKeys, bytes[] memory _signatures) =
                    ValidatorKeys.getKeys(operators[idx].index, operators[idx].funded, operators[idx].picked);
                emit FundedValidatorKeys(operators[idx].index, _publicKeys, false);
                publicKeys = _concatenateByteArrays(publicKeys, _publicKeys);
                signatures = _concatenateByteArrays(signatures, _signatures);
                (OperatorsV2.get(operators[idx].index)).funded += operators[idx].picked;
            }
        }
    }

    /// @notice Internal utility to get the count of active validators during the exit selection process
    /// @param _operator The Operator structure in memory
    /// @return The count of active validators for the operator
    function _getActiveValidatorCountForExitRequests(OperatorsV2.CachedExitableOperator memory _operator)
        internal
        pure
        returns (uint32)
    {
        return _operator.funded - (_operator.requestedExits + _operator.picked);
    }

    /// @notice Internal utility to pick the next validator counts to exit for every operator
    /// @param _count The count of validators to request exits for
    function _pickNextValidatorsToExitFromActiveOperators(uint256 _count) internal returns (uint256) {
        (OperatorsV2.CachedExitableOperator[] memory operators, uint256 exitableOperatorCount) =
            OperatorsV2.getAllExitable();

        if (exitableOperatorCount == 0) {
            return 0;
        }

        uint256 initialExitRequestDemand = _count;
        uint256 totalRequestedExitsValue = TotalValidatorExitsRequested.get();
        uint256 totalRequestedExitsCopy = totalRequestedExitsValue;

        // we loop to find the highest count of active validators, the number of operators that have this amount and the second highest amount
        while (_count > 0) {
            uint32 highestActiveCount = 0;
            uint32 secondHighestActiveCount = 0;
            uint32 siblings = 0;

            for (uint256 idx = 0; idx < exitableOperatorCount;) {
                uint32 activeCount = _getActiveValidatorCountForExitRequests(operators[idx]);

                if (activeCount == highestActiveCount) {
                    ++siblings;
                } else if (activeCount > highestActiveCount) {
                    secondHighestActiveCount = highestActiveCount;
                    highestActiveCount = activeCount;
                    siblings = 1;
                } else if (activeCount > secondHighestActiveCount) {
                    secondHighestActiveCount = activeCount;
                }

                unchecked {
                    ++idx;
                }
            }

            // we exited all exitable validators
            if (highestActiveCount == 0) {
                break;
            }
            // The optimal amount is how much we should dispatch to all the operators with the highest count for them to get the same amount
            // of active validators as the second highest count. We then take the minimum between this value and the total we need to exit
            uint32 optimalTotalDispatchCount =
                uint32(LibUint256.min((highestActiveCount - secondHighestActiveCount) * siblings, _count));

            // We lookup the operators again to assign the exit requests
            uint256 rest = optimalTotalDispatchCount % siblings;
            uint32 baseExitRequestAmount = optimalTotalDispatchCount / siblings;
            for (uint256 idx = 0; idx < exitableOperatorCount;) {
                if (_getActiveValidatorCountForExitRequests(operators[idx]) == highestActiveCount) {
                    uint32 additionalRequestedExits = baseExitRequestAmount + (rest > 0 ? 1 : 0);
                    operators[idx].picked += additionalRequestedExits;
                    if (rest > 0) {
                        --rest;
                    }
                }
                unchecked {
                    ++idx;
                }
            }

            totalRequestedExitsValue += optimalTotalDispatchCount;
            _count -= optimalTotalDispatchCount;
        }

        // We loop over the operators and apply the change, also emit the exit request event
        for (uint256 idx = 0; idx < exitableOperatorCount;) {
            if (operators[idx].picked > 0) {
                uint256 opIndex = operators[idx].index;
                uint32 newRequestedExits = operators[idx].requestedExits + operators[idx].picked;

                OperatorsV2.get(opIndex).requestedExits = newRequestedExits;
                emit RequestedValidatorExits(opIndex, newRequestedExits);
            }

            unchecked {
                ++idx;
            }
        }

        if (totalRequestedExitsValue != totalRequestedExitsCopy) {
            _setTotalValidatorExitsRequested(totalRequestedExitsCopy, totalRequestedExitsValue);
        }

        return initialExitRequestDemand - _count;
    }

    /// @notice Internal utility to set the total validator exits requested by the system
    /// @param _currentValue The current value of the total validator exits requested
    /// @param _newValue The new value of the total validator exits requested
    function _setTotalValidatorExitsRequested(uint256 _currentValue, uint256 _newValue) internal {
        TotalValidatorExitsRequested.set(_newValue);
        emit SetTotalValidatorExitsRequested(_currentValue, _newValue);
    }
}

File 79 of 116 : Operators.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Operators Storage
/// @notice Utility to manage the Operators in storage
/// @notice This state variable is deprecated and was kept due to migration logic needs
library OperatorsV1 {
    /// @notice Storage slot of the Operators
    bytes32 internal constant OPERATORS_SLOT = bytes32(uint256(keccak256("river.state.operators")) - 1);

    /// @notice The Operator structure in storage
    struct Operator {
        /// @custom:attribute True if the operator is active and allowed to operate on River
        bool active;
        /// @custom:attribute Display name of the operator
        string name;
        /// @custom:attribute Address of the operator
        address operator;
        /// @dev The following values respect this invariant:
        /// @dev     keys >= limit >= funded >= stopped

        /// @custom:attribute Staking limit of the operator
        uint256 limit;
        /// @custom:attribute The count of funded validators
        uint256 funded;
        /// @custom:attribute The total count of keys of the operator
        uint256 keys;
        /// @custom:attribute The count of stopped validators. Stopped validators are validators
        ///                   that exited the consensus layer (voluntary or slashed)
        uint256 stopped;
        uint256 latestKeysEditBlockNumber;
    }

    /// @notice The Operator structure when loaded in memory
    struct CachedOperator {
        /// @custom:attribute True if the operator is active and allowed to operate on River
        bool active;
        /// @custom:attribute Display name of the operator
        string name;
        /// @custom:attribute Address of the operator
        address operator;
        /// @custom:attribute Staking limit of the operator
        uint256 limit;
        /// @custom:attribute The count of funded validators
        uint256 funded;
        /// @custom:attribute The total count of keys of the operator
        uint256 keys;
        /// @custom:attribute The count of stopped validators
        uint256 stopped;
        /// @custom:attribute The count of stopped validators. Stopped validators are validators
        ///                   that exited the consensus layer (voluntary or slashed)
        uint256 index;
        /// @custom:attribute The amount of picked keys, buffer used before changing funded in storage
        uint256 picked;
    }

    /// @notice The structure at the storage slot
    struct SlotOperator {
        /// @custom:attribute Array containing all the operators
        Operator[] value;
    }

    /// @notice The operator was not found
    /// @param index The provided index
    error OperatorNotFound(uint256 index);

    /// @notice Retrieve the operator in storage
    /// @param _index The index of the operator
    /// @return The Operator structure
    function get(uint256 _index) internal view returns (Operator storage) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        if (r.value.length <= _index) {
            revert OperatorNotFound(_index);
        }

        return r.value[_index];
    }

    /// @notice Retrieve the operator count in storage
    /// @return The count of operators in storage
    function getCount() internal view returns (uint256) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        return r.value.length;
    }

    /// @notice Retrieve all the active operators
    /// @return The list of active operator structures
    function getAllActive() internal view returns (Operator[] memory) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        uint256 activeCount = 0;
        uint256 operatorCount = r.value.length;

        for (uint256 idx = 0; idx < operatorCount;) {
            if (r.value[idx].active) {
                unchecked {
                    ++activeCount;
                }
            }
            unchecked {
                ++idx;
            }
        }

        Operator[] memory activeOperators = new Operator[](activeCount);

        uint256 activeIdx = 0;
        for (uint256 idx = 0; idx < operatorCount;) {
            if (r.value[idx].active) {
                activeOperators[activeIdx] = r.value[idx];
                unchecked {
                    ++activeIdx;
                }
            }
            unchecked {
                ++idx;
            }
        }

        return activeOperators;
    }

    /// @notice Retrieve all the active and fundable operators
    /// @return The list of active and fundable operators
    function getAllFundable() internal view returns (CachedOperator[] memory) {
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        uint256 activeCount = 0;
        uint256 operatorCount = r.value.length;

        for (uint256 idx = 0; idx < operatorCount;) {
            if (_hasFundableKeys(r.value[idx])) {
                unchecked {
                    ++activeCount;
                }
            }
            unchecked {
                ++idx;
            }
        }

        CachedOperator[] memory activeOperators = new CachedOperator[](activeCount);

        uint256 activeIdx = 0;
        for (uint256 idx = 0; idx < operatorCount;) {
            Operator memory op = r.value[idx];
            if (_hasFundableKeys(op)) {
                activeOperators[activeIdx] = CachedOperator({
                    active: op.active,
                    name: op.name,
                    operator: op.operator,
                    limit: op.limit,
                    funded: op.funded,
                    keys: op.keys,
                    stopped: op.stopped,
                    index: idx,
                    picked: 0
                });
                unchecked {
                    ++activeIdx;
                }
            }
            unchecked {
                ++idx;
            }
        }

        return activeOperators;
    }

    /// @notice Add a new operator in storage
    /// @param _newOperator Value of the new operator
    /// @return The size of the operator array after the operation
    function push(Operator memory _newOperator) internal returns (uint256) {
        LibSanitize._notZeroAddress(_newOperator.operator);
        LibSanitize._notEmptyString(_newOperator.name);
        bytes32 slot = OPERATORS_SLOT;

        SlotOperator storage r;

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

        r.value.push(_newOperator);

        return r.value.length;
    }

    /// @notice Atomic operation to set the key count and update the latestKeysEditBlockNumber field at the same time
    /// @param _index The operator index
    /// @param _newKeys The new value for the key count
    function setKeys(uint256 _index, uint256 _newKeys) internal {
        Operator storage op = get(_index);

        op.keys = _newKeys;
        op.latestKeysEditBlockNumber = block.number;
    }

    /// @notice Checks if an operator is active and has fundable keys
    /// @param _operator The operator details
    /// @return True if active and fundable
    function _hasFundableKeys(OperatorsV1.Operator memory _operator) internal pure returns (bool) {
        return (_operator.active && _operator.limit > _operator.funded);
    }
}

File 80 of 116 : TotalValidatorExitsRequested.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title TotalValidatorExitsRequested Storage
/// @notice This value is the amount of performed exit requests, only increased when there is current exit demand
/// @notice Utility to manage the TotalValidatorExitsRequested in storage
library TotalValidatorExitsRequested {
    /// @notice Storage slot of the TotalValidatorExitsRequested
    bytes32 internal constant TOTAL_VALIDATOR_EXITS_REQUESTED_SLOT =
        bytes32(uint256(keccak256("river.state.totalValidatorExitsRequested")) - 1);

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

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

File 81 of 116 : CurrentValidatorExitsDemand.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title CurrentValidatorExitsDemand Storage
/// @notice This value controls the current demand for exits that still need to be triggered
/// @notice in order to notify the operators
/// @notice Utility to manage the CurrentValidatorExitsDemand in storage
library CurrentValidatorExitsDemand {
    /// @notice Storage slot of the CurrentValidatorExitsDemand
    bytes32 internal constant CURRENT_VALIDATOR_EXITS_DEMAND_SLOT =
        bytes32(uint256(keccak256("river.state.currentValidatorExitsDemand")) - 1);

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

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

File 82 of 116 : OperatorsRegistry_FundedKeyEventRebroadcasting_KeyIndex.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

library OperatorsRegistry_FundedKeyEventRebroadcasting_KeyIndex {
    bytes32 internal constant KEY_INDEX_SLOT =
        bytes32(uint256(keccak256("river.state.migration.operatorsRegistry.fundedKeyEventRebroadcasting.keyIndex")) - 1);

    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(KEY_INDEX_SLOT);
    }

    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(KEY_INDEX_SLOT, _newValue);
    }
}

File 83 of 116 : OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

library OperatorsRegistry_FundedKeyEventRebroadcasting_OperatorIndex {
    bytes32 internal constant OPERATOR_INDEX_SLOT = bytes32(
        uint256(keccak256("river.state.migration.operatorsRegistry.fundedKeyEventRebroadcasting.operatorIndex")) - 1
    );

    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(OPERATOR_INDEX_SLOT);
    }

    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(OPERATOR_INDEX_SLOT, _newValue);
    }
}

File 84 of 116 : ELFeeRecipient.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IRiver.1.sol";
import "./interfaces/IELFeeRecipient.1.sol";

import "./libraries/LibUint256.sol";

import "./Initializable.sol";

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

/// @title Execution Layer Fee Recipient (v1)
/// @author Kiln
/// @notice This contract receives all the execution layer fees from the proposed blocks + bribes
contract ELFeeRecipientV1 is Initializable, IELFeeRecipientV1 {
    /// @inheritdoc IELFeeRecipientV1
    function initELFeeRecipientV1(address _riverAddress) external init(0) {
        RiverAddress.set(_riverAddress);
        emit SetRiver(_riverAddress);
    }

    /// @inheritdoc IELFeeRecipientV1
    function pullELFees(uint256 _maxAmount) external {
        address river = RiverAddress.get();
        if (msg.sender != river) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        uint256 amount = LibUint256.min(_maxAmount, address(this).balance);

        if (amount > 0) {
            IRiverV1(payable(river)).sendELFees{value: amount}();
        }
    }

    /// @inheritdoc IELFeeRecipientV1
    receive() external payable {
        this;
    }

    /// @inheritdoc IELFeeRecipientV1
    fallback() external payable {
        revert InvalidCall();
    }
}

File 85 of 116 : CoverageFund.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IRiver.1.sol";
import "./interfaces/IAllowlist.1.sol";
import "./interfaces/ICoverageFund.1.sol";

import "./libraries/LibUint256.sol";
import "./libraries/LibAllowlistMasks.sol";

import "./Initializable.sol";

import "./state/shared/RiverAddress.sol";
import "./state/slashingCoverage/BalanceForCoverage.sol";

/// @title Coverage Fund (v1)
/// @author Kiln
/// @notice This contract receive donations for the slashing coverage fund and pull the funds into river
/// @notice This contract acts as a temporary buffer for funds that should be pulled in case of a loss of money on the consensus layer due to slashing events.
/// @notice There is no fee taken on these funds, they are entirely distributed to the LsETH holders, and no shares will get minted.
/// @notice Funds will be distributed by increasing the underlying value of every LsETH share.
/// @notice The fund will be called on every report and if eth is available in the contract, River will attempt to pull as much
/// @notice ETH as possible. This maximum is defined by the upper bound allowed by the Oracle. This means that it might take multiple
/// @notice reports for funds to be pulled entirely into the system due to this upper bound, ensuring a lower secondary market impact.
/// @notice The value provided to this contract is computed off-chain and provided manually by Alluvial or any authorized insurance entity.
/// @notice The Coverage funds are pulled upon an oracle report, after the ELFees have been pulled in the system, if there is a margin left
/// @notice before crossing the upper bound. The reason behind this is to favor the revenue stream, that depends on market and network usage, while
/// @notice the coverage fund will be pulled after the revenue stream, and there won't be any commission on the eth pulled.
/// @notice Once a Slashing event occurs, the team will do its best to inject the recovery funds in at maximum 365 days
/// @notice The entities allowed to donate are selected by the team. It will mainly be treasury entities or insurance protocols able to fill this coverage fund properly.
contract CoverageFundV1 is Initializable, ICoverageFundV1 {
    /// @inheritdoc ICoverageFundV1
    function initCoverageFundV1(address _riverAddress) external init(0) {
        RiverAddress.set(_riverAddress);
        emit SetRiver(_riverAddress);
    }

    /// @inheritdoc ICoverageFundV1
    function pullCoverageFunds(uint256 _maxAmount) external {
        address river = RiverAddress.get();
        if (msg.sender != river) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        uint256 amount = LibUint256.min(_maxAmount, BalanceForCoverage.get());

        if (amount > 0) {
            BalanceForCoverage.set(BalanceForCoverage.get() - amount);
            IRiverV1(payable(river)).sendCoverageFunds{value: amount}();
        }
    }

    /// @inheritdoc ICoverageFundV1
    function donate() external payable {
        if (msg.value == 0) {
            revert EmptyDonation();
        }
        BalanceForCoverage.set(BalanceForCoverage.get() + msg.value);

        IAllowlistV1 allowlist = IAllowlistV1(IRiverV1(payable(RiverAddress.get())).getAllowlist());
        allowlist.onlyAllowed(msg.sender, LibAllowlistMasks.DONATE_MASK);

        emit Donate(msg.sender, msg.value);
    }

    /// @inheritdoc ICoverageFundV1
    receive() external payable {
        revert InvalidCall();
    }

    /// @inheritdoc ICoverageFundV1
    fallback() external payable {
        revert InvalidCall();
    }
}

File 86 of 116 : BalanceForCoverage.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Balance For Coverage Value Storage
/// @notice Utility to manage the Balance For Coverrage value in storage
library BalanceForCoverage {
    /// @notice Storage slot of the Balance For Coverage Address
    bytes32 internal constant BALANCE_FOR_COVERAGE_SLOT =
        bytes32(uint256(keccak256("river.state.balanceForCoverage")) - 1);

    /// @notice Get the Balance for Coverage value
    /// @return The balance for coverage value
    function get() internal view returns (uint256) {
        return LibUnstructuredStorage.getStorageUint256(BALANCE_FOR_COVERAGE_SLOT);
    }

    /// @notice Sets the Balance for Coverage value
    /// @param _newValue New Balance for Coverage value
    function set(uint256 _newValue) internal {
        LibUnstructuredStorage.setStorageUint256(BALANCE_FOR_COVERAGE_SLOT, _newValue);
    }
}

File 87 of 116 : Allowlist.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IAllowlist.1.sol";

import "./libraries/LibAllowlistMasks.sol";
import "./Initializable.sol";
import "./Administrable.sol";

import "./state/allowlist/AllowerAddress.sol";
import "./state/allowlist/Allowlist.sol";

/// @title Allowlist (v1)
/// @author Kiln
/// @notice This contract handles the list of allowed recipients.
/// @notice All accounts have an uint256 value associated with their addresses where
/// @notice each bit represents a right in the system. The DENY_MASK defined the mask
/// @notice used to identify if the denied bit is on, preventing users from interacting
/// @notice with the system
contract AllowlistV1 is IAllowlistV1, Initializable, Administrable {
    /// @inheritdoc IAllowlistV1
    function initAllowlistV1(address _admin, address _allower) external init(0) {
        _setAdmin(_admin);
        AllowerAddress.set(_allower);
        emit SetAllower(_allower);
    }

    /// @inheritdoc IAllowlistV1
    function getAllower() external view returns (address) {
        return AllowerAddress.get();
    }

    /// @inheritdoc IAllowlistV1
    function isAllowed(address _account, uint256 _mask) external view returns (bool) {
        uint256 userPermissions = Allowlist.get(_account);
        if (userPermissions & LibAllowlistMasks.DENY_MASK == LibAllowlistMasks.DENY_MASK) {
            return false;
        }
        return userPermissions & _mask == _mask;
    }

    /// @inheritdoc IAllowlistV1
    function isDenied(address _account) external view returns (bool) {
        return Allowlist.get(_account) & LibAllowlistMasks.DENY_MASK == LibAllowlistMasks.DENY_MASK;
    }

    /// @inheritdoc IAllowlistV1
    function hasPermission(address _account, uint256 _mask) external view returns (bool) {
        return Allowlist.get(_account) & _mask == _mask;
    }

    /// @inheritdoc IAllowlistV1
    function getPermissions(address _account) external view returns (uint256) {
        return Allowlist.get(_account);
    }

    /// @inheritdoc IAllowlistV1
    function onlyAllowed(address _account, uint256 _mask) external view {
        uint256 userPermissions = Allowlist.get(_account);
        if (userPermissions & LibAllowlistMasks.DENY_MASK == LibAllowlistMasks.DENY_MASK) {
            revert Denied(_account);
        }
        if (userPermissions & _mask != _mask) {
            revert LibErrors.Unauthorized(_account);
        }
    }

    /// @inheritdoc IAllowlistV1
    function setAllower(address _newAllowerAddress) external onlyAdmin {
        AllowerAddress.set(_newAllowerAddress);
        emit SetAllower(_newAllowerAddress);
    }

    /// @inheritdoc IAllowlistV1
    function allow(address[] calldata _accounts, uint256[] calldata _permissions) external {
        if (msg.sender != AllowerAddress.get() && msg.sender != _getAdmin()) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        if (_accounts.length == 0) {
            revert InvalidAlloweeCount();
        }

        if (_accounts.length != _permissions.length) {
            revert MismatchedAlloweeAndStatusCount();
        }

        for (uint256 i = 0; i < _accounts.length;) {
            LibSanitize._notZeroAddress(_accounts[i]);
            Allowlist.set(_accounts[i], _permissions[i]);
            unchecked {
                ++i;
            }
        }

        emit SetAllowlistPermissions(_accounts, _permissions);
    }
}

File 88 of 116 : AllowerAddress.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

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

/// @title Allower Address Storage
/// @notice Utility to manage the Allower Address in storage
library AllowerAddress {
    /// @notice Storage slot of the Allower Address
    bytes32 internal constant ALLOWER_ADDRESS_SLOT = bytes32(uint256(keccak256("river.state.allowerAddress")) - 1);

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

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

File 89 of 116 : Allowlist.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Allowlist Storage
/// @notice Utility to manage the Allowlist mapping in storage
library Allowlist {
    /// @notice Storage slot of the Allowlist mapping
    bytes32 internal constant ALLOWLIST_SLOT = bytes32(uint256(keccak256("river.state.allowlist")) - 1);

    /// @notice Structure stored in storage slot
    struct Slot {
        /// @custom:attribute Mapping keeping track of permissions per account
        mapping(address => uint256) value;
    }

    /// @notice Retrieve the Allowlist value of an account
    /// @param _account The account to verify
    /// @return The Allowlist value
    function get(address _account) internal view returns (uint256) {
        bytes32 slot = ALLOWLIST_SLOT;

        Slot storage r;

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

        return r.value[_account];
    }

    /// @notice Sets the Allowlist value of an account
    /// @param _account The account value to set
    /// @param _status The value to set
    function set(address _account, uint256 _status) internal {
        bytes32 slot = ALLOWLIST_SLOT;

        Slot storage r;

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

        r.value[_account] = _status;
    }
}

File 90 of 116 : DepositContractMock.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../interfaces/IDepositContract.sol";

contract DepositContractMock is IDepositContract {
    event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);

    uint256 public depositCount;
    address public receiver;

    constructor(address _receiver) {
        receiver = _receiver;
    }

    function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
        ret = new bytes(8);
        bytes8 bytesValue = bytes8(value);
        // Byteswapping during copying to bytes.
        ret[0] = bytesValue[7];
        ret[1] = bytesValue[6];
        ret[2] = bytesValue[5];
        ret[3] = bytesValue[4];
        ret[4] = bytesValue[3];
        ret[5] = bytesValue[2];
        ret[6] = bytesValue[1];
        ret[7] = bytesValue[0];
    }

    function deposit(bytes calldata pubkey, bytes calldata withdrawalCredentials, bytes calldata signature, bytes32)
        external
        payable
    {
        emit DepositEvent(
            pubkey,
            withdrawalCredentials,
            to_little_endian_64(uint64(msg.value / 1 gwei)),
            signature,
            to_little_endian_64(uint64(depositCount))
        );
        depositCount += 1;
        (bool sent,) = receiver.call{value: address(this).balance}("");
        require(sent, "Fund transfer failed");
    }
}

File 91 of 116 : ERC20VestableVotesUpgradeable.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";

import "../interfaces/components/IERC20VestableVotesUpgradeable.1.sol";

import "../state/tlc/VestingSchedules.2.sol";
import "../state/tlc/IgnoreGlobalUnlockSchedule.sol";

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

/// @title ERC20VestableVotesUpgradeableV1
/// @author Alluvial
/// @notice This is an ERC20 extension that
/// @notice   - can be used as source of vote power (inherited from OpenZeppelin ERC20VotesUpgradeable)
/// @notice   - can delegate vote power from an account to another account (inherited from OpenZeppelin ERC20VotesUpgradeable)
/// @notice   - can manage token vestings: ownership is progressively transferred to a beneficiary according to a vesting schedule
/// @notice   - keeps a history (checkpoints) of each account's vote power
/// @notice
/// @notice Notes from OpenZeppelin [ERC20VotesUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol)
/// @notice   - vote power can be delegated either by calling the {delegate} function, or by providing a signature to be used with {delegateBySig}
/// @notice   - keeps a history (checkpoints) of each account's vote power
/// @notice   - power can be queried through the public accessors {getVotes} and {getPastVotes}.
/// @notice   - by default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
/// @notice requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
/// @notice
/// @notice Notes about token vesting
/// @notice   - any token holder can call the method {createVestingSchedule} in order to transfer tokens to a beneficiary according to a vesting schedule. When
/// @notice     creating a vesting schedule, tokens are transferred to an escrow that holds the token while the vesting progresses. Voting power of the escrowed token is delegated to the
/// @notice     beneficiary or a delegatee account set by the vesting schedule creator
/// @notice   - the schedule beneficiary call {releaseVestingSchedule} to get vested tokens transferred from escrow
/// @notice   - the schedule creator can revoke a revocable schedule by calling {revokeVestingSchedule} in which case the non-vested tokens are transfered from the escrow back to the creator
/// @notice   - the schedule beneficiary can delegate escrow voting power to any account by calling {delegateVestingEscrow}
/// @notice
/// @notice Vesting schedule attributes are
/// @notice   - start : start time of the vesting period
/// @notice   - cliff duration: duration before which first tokens gets ownable
/// @notice   - total duration: duration of the entire vesting (sum of all vesting period durations)
/// @notice   - period duration: duration of a single period of vesting
/// @notice   - lock duration: duration before tokens gets unlocked. can exceed the duration of the vesting chedule
/// @notice   - amount: amount of tokens granted by the vesting schedule
/// @notice   - beneficiary: beneficiary of tokens after they are releaseVestingScheduled
/// @notice   - revocable: whether the schedule can be revoked
/// @notice   - ignoreGlobalUnlockSchedule: whether the schedule should ignore the global unlock schedule
/// @notice
/// @notice Vesting schedule
/// @notice   - if currentTime < cliff: vestedToken = 0
/// @notice   - if cliff <= currentTime < end: vestedToken = (vestedPeriodCount(currentTime) * periodDuration * amount) / totalDuration
/// @notice   - if end < currentTime: vestedToken = amount
/// @notice
/// @notice Global unlock schedule
/// @notice   - the global unlock schedule releases 1/24th of the total scheduled amount every month after the local lock end
/// @notice   - the local lock end is the end of the lock period of the vesting schedule
/// @notice   - the global unlock schedule is ignored if the vesting schedule has the ignoreGlobalUnlockSchedule flag set to true
/// @notice   - the global unlock schedule is only a cap on the vested funds that can be withdrawn, it does not alter the vesting
/// @notice
/// @notice Remark: After cliff new tokens get vested at the end of each period
/// @notice
/// @notice Vested token & lock period
/// @notice   - a vested token is a token that will be eventually releasable from the escrow to the beneficiary once the lock period is over
/// @notice   - lock period prevents beneficiary from releasing vested tokens before the lock period ends. Vested tokens
/// @notice will eventually be releasable once the lock period is over
/// @notice
/// @notice Example: Joe gets a vesting starting on Jan 1st 2022 with duration of 1 year and a lock period of 2 years.
/// @notice On Jan 1st 2023, Joe will have all tokens vested but can not yet release it due to the lock period.
/// @notice On Jan 1st 2024, lock period is over and Joe can release all tokens.
abstract contract ERC20VestableVotesUpgradeableV1 is
    Initializable,
    IERC20VestableVotesUpgradeableV1,
    ERC20VotesUpgradeable
{
    // internal used to compute the address of the escrow
    bytes32 internal constant ESCROW = bytes32(uint256(keccak256("escrow")) - 1);

    function __ERC20VestableVotes_init() internal onlyInitializing {}

    function __ERC20VestableVotes_init_unchained() internal onlyInitializing {}

    /// @notice This method migrates the state of the vesting schedules from V1 to V2
    /// @dev This method should be used if deployment with the old version using V1 state models is upgraded
    function migrateVestingSchedulesFromV1ToV2() internal {
        if (VestingSchedulesV2.getCount() == 0) {
            uint256 existingV1VestingSchedules = VestingSchedulesV1.getCount();
            for (uint256 idx; idx < existingV1VestingSchedules;) {
                uint256 scheduleAmount = VestingSchedulesV1.get(idx).amount;
                uint256 releasedAmount =
                    scheduleAmount - LibUint256.min(balanceOf(_deterministicVestingEscrow(idx)), scheduleAmount);
                VestingSchedulesV2.migrateVestingScheduleFromV1(idx, releasedAmount);
                unchecked {
                    ++idx;
                }
            }
        }
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function getVestingSchedule(uint256 _index) external view returns (VestingSchedulesV2.VestingSchedule memory) {
        return VestingSchedulesV2.get(_index);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function isGlobalUnlockedScheduleIgnored(uint256 _index) external view returns (bool) {
        return IgnoreGlobalUnlockSchedule.get(_index);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function getVestingScheduleCount() external view returns (uint256) {
        return VestingSchedulesV2.getCount();
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function vestingEscrow(uint256 _index) external view returns (address) {
        return _deterministicVestingEscrow(_index);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function computeVestingReleasableAmount(uint256 _index) external view returns (uint256) {
        VestingSchedulesV2.VestingSchedule memory vestingSchedule = VestingSchedulesV2.get(_index);
        return _computeVestingReleasableAmount(vestingSchedule, false, _index);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function computeVestingVestedAmount(uint256 _index) external view returns (uint256) {
        VestingSchedulesV2.VestingSchedule memory vestingSchedule = VestingSchedulesV2.get(_index);
        return _computeVestedAmount(vestingSchedule, LibUint256.min(_getCurrentTime(), vestingSchedule.end));
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function createVestingSchedule(
        uint64 _start,
        uint32 _cliffDuration,
        uint32 _duration,
        uint32 _periodDuration,
        uint32 _lockDuration,
        bool _revocable,
        uint256 _amount,
        address _beneficiary,
        address _delegatee,
        bool _ignoreGlobalUnlockSchedule
    ) external returns (uint256) {
        return _createVestingSchedule(
            msg.sender,
            _beneficiary,
            _delegatee,
            _start,
            _cliffDuration,
            _duration,
            _periodDuration,
            _lockDuration,
            _revocable,
            _amount,
            _ignoreGlobalUnlockSchedule
        );
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function revokeVestingSchedule(uint256 _index, uint64 _end) external returns (uint256) {
        return _revokeVestingSchedule(_index, _end);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function releaseVestingSchedule(uint256 _index) external returns (uint256) {
        return _releaseVestingSchedule(_index);
    }

    /// @inheritdoc IERC20VestableVotesUpgradeableV1
    function delegateVestingEscrow(uint256 _index, address _delegatee) external returns (bool) {
        return _delegateVestingEscrow(_index, _delegatee);
    }

    /// @notice Creates a new vesting schedule
    /// @param _creator creator of the token vesting
    /// @param _beneficiary beneficiary of tokens after they are releaseVestingScheduled
    /// @param _delegatee address of the delegate escrowed tokens votes to (if address(0) then it defaults to the beneficiary)
    /// @param _start start time of the vesting period
    /// @param _cliffDuration duration before which first tokens gets ownable
    /// @param _duration duration of the entire vesting (sum of all vesting period durations)
    /// @param _periodDuration duration of a single period of vesting
    /// @param _lockDuration duration before tokens gets unlocked. can exceed the duration of the vesting chedule
    /// @param _revocable whether the schedule can be revoked
    /// @param _amount amount of tokens granted by the vesting schedule
    /// @param _ignoreGlobalUnlockSchedule whether the schedule should ignore the global unlock schedule
    /// @return index of the created vesting schedule
    function _createVestingSchedule(
        address _creator,
        address _beneficiary,
        address _delegatee,
        uint64 _start,
        uint32 _cliffDuration,
        uint32 _duration,
        uint32 _periodDuration,
        uint32 _lockDuration,
        bool _revocable,
        uint256 _amount,
        bool _ignoreGlobalUnlockSchedule
    ) internal returns (uint256) {
        if (balanceOf(_creator) < _amount) {
            revert UnsufficientVestingScheduleCreatorBalance();
        }

        // validate schedule parameters
        if (_beneficiary == address(0)) {
            revert InvalidVestingScheduleParameter("Vesting schedule beneficiary must be non zero address");
        }

        if (_duration == 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule duration must be > 0");
        }

        if (_amount == 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule amount must be > 0");
        }

        if (_periodDuration == 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule period must be > 0");
        }

        if (_duration % _periodDuration > 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule duration must split in exact periods");
        }

        if (_cliffDuration % _periodDuration > 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule cliff duration must split in exact periods");
        }

        if (_cliffDuration > _duration) {
            revert InvalidVestingScheduleParameter(
                "Vesting schedule duration must be greater than or equal to the cliff duration"
            );
        }

        if ((_amount * _periodDuration) / _duration == 0) {
            revert InvalidVestingScheduleParameter("Vesting schedule amount too low for duration and period");
        }

        // if input start time is 0 then default to the current block time
        if (_start == 0) {
            _start = uint64(block.timestamp);
        }

        // create new vesting schedule
        VestingSchedulesV2.VestingSchedule memory vestingSchedule = VestingSchedulesV2.VestingSchedule({
            start: _start,
            end: _start + _duration,
            lockDuration: _lockDuration,
            cliffDuration: _cliffDuration,
            duration: _duration,
            periodDuration: _periodDuration,
            amount: _amount,
            creator: _creator,
            beneficiary: _beneficiary,
            revocable: _revocable,
            releasedAmount: 0
        });
        uint256 index = VestingSchedulesV2.push(vestingSchedule) - 1;

        IgnoreGlobalUnlockSchedule.set(index, _ignoreGlobalUnlockSchedule);

        // compute escrow address that will hold the token during the vesting
        address escrow = _deterministicVestingEscrow(index);

        // transfer tokens to the escrow
        _transfer(_creator, escrow, _amount);

        // delegate escrow tokens
        if (_delegatee == address(0)) {
            // default delegatee to beneficiary address
            _delegate(escrow, _beneficiary);
        } else {
            _delegate(escrow, _delegatee);
        }

        emit CreatedVestingSchedule(index, _creator, _beneficiary, _amount);

        return index;
    }

    /// @notice Revoke vesting schedule
    /// @param _index Index of the vesting schedule to revoke
    /// @param _end End date for the schedule
    /// @return returnedAmount amount returned to the vesting schedule creator
    function _revokeVestingSchedule(uint256 _index, uint64 _end) internal returns (uint256) {
        if (_end == 0) {
            // if end time is 0 then default to current block time
            _end = uint64(block.timestamp);
        } else if (_end < block.timestamp) {
            revert VestingScheduleNotRevocableInPast();
        }

        VestingSchedulesV2.VestingSchedule storage vestingSchedule = VestingSchedulesV2.get(_index);
        if (!vestingSchedule.revocable) {
            revert VestingScheduleNotRevocable();
        }

        // revoked end date MUST be after vesting schedule start and before current end
        if ((_end < vestingSchedule.start) || (vestingSchedule.end < _end)) {
            revert InvalidRevokedVestingScheduleEnd();
        }

        // only creator can revoke vesting schedule
        if (vestingSchedule.creator != msg.sender) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        // return tokens that will never be vested to creator
        uint256 vestedAmountAtOldEnd = _computeVestedAmount(vestingSchedule, vestingSchedule.end);
        uint256 vestedAmountAtNewEnd = _computeVestedAmount(vestingSchedule, _end);
        uint256 returnedAmount = vestedAmountAtOldEnd - vestedAmountAtNewEnd;
        if (returnedAmount > 0) {
            address escrow = _deterministicVestingEscrow(_index);
            _transfer(escrow, msg.sender, returnedAmount);
        }

        // set schedule end
        vestingSchedule.end = uint64(_end);

        emit RevokedVestingSchedule(_index, returnedAmount, _end);

        return returnedAmount;
    }

    /// @notice Release vesting schedule
    /// @param _index Index of the vesting schedule to release
    /// @return released amount
    function _releaseVestingSchedule(uint256 _index) internal returns (uint256) {
        VestingSchedulesV2.VestingSchedule storage vestingSchedule = VestingSchedulesV2.get(_index);

        // only beneficiary can release
        if (msg.sender != vestingSchedule.beneficiary) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        // compute releasable amount (taking into account local lock and global unlock schedule if it applies)
        uint256 releasableAmount = _computeVestingReleasableAmount(vestingSchedule, true, _index);
        if (releasableAmount == 0) {
            revert ZeroReleasableAmount();
        }

        address escrow = _deterministicVestingEscrow(_index);

        // transfer all releasable token to the beneficiary
        _transfer(escrow, msg.sender, releasableAmount);

        // increase released amount as per the release
        vestingSchedule.releasedAmount += releasableAmount;

        emit ReleasedVestingSchedule(_index, releasableAmount);

        return releasableAmount;
    }

    /// @notice Delegate vesting escrowed tokens
    /// @param _index index of the vesting schedule
    /// @param _delegatee address to delegate the token to
    /// @return True on success
    function _delegateVestingEscrow(uint256 _index, address _delegatee) internal returns (bool) {
        VestingSchedulesV2.VestingSchedule storage vestingSchedule = VestingSchedulesV2.get(_index);

        // only beneficiary can delegate
        if (msg.sender != vestingSchedule.beneficiary) {
            revert LibErrors.Unauthorized(msg.sender);
        }

        // update delegatee
        address escrow = _deterministicVestingEscrow(_index);
        address oldDelegatee = delegates(escrow);
        _delegate(escrow, _delegatee);

        emit DelegatedVestingEscrow(_index, oldDelegatee, _delegatee, msg.sender);

        return true;
    }

    /// @notice Internal utility to compute the unique escrow deterministic address
    /// @param _index index of the vesting schedule
    /// @return escrow The deterministic escrow address for the vesting schedule index
    function _deterministicVestingEscrow(uint256 _index) internal view returns (address escrow) {
        bytes32 hash = keccak256(abi.encodePacked(address(this), ESCROW, _index));
        return address(uint160(uint256(hash)));
    }

    /// @notice Computes the releasable amount of tokens for a vesting schedule.
    /// @param _vestingSchedule vesting schedule to compute releasable tokens for
    /// @param  _revertIfLocked if true will revert if the schedule is locked
    /// @param _index index of the vesting schedule
    /// @return amount of release tokens
    function _computeVestingReleasableAmount(
        VestingSchedulesV2.VestingSchedule memory _vestingSchedule,
        bool _revertIfLocked,
        uint256 _index
    ) internal view returns (uint256) {
        uint256 time = _getCurrentTime();
        if (time < (_vestingSchedule.start + _vestingSchedule.lockDuration)) {
            if (_revertIfLocked) {
                revert VestingScheduleIsLocked();
            } else {
                return 0;
            }
        }
        uint256 releasedAmount = _vestingSchedule.releasedAmount;
        uint256 vestedAmount =
            _computeVestedAmount(_vestingSchedule, time > _vestingSchedule.end ? _vestingSchedule.end : time);
        if (vestedAmount > releasedAmount) {
            if (!IgnoreGlobalUnlockSchedule.get(_index)) {
                uint256 globalUnlocked = _computeGlobalUnlocked(
                    _vestingSchedule.amount, time - (_vestingSchedule.start + _vestingSchedule.lockDuration)
                );
                if (releasedAmount > globalUnlocked) {
                    revert GlobalUnlockUnderlfow();
                }
                return LibUint256.min(vestedAmount, globalUnlocked) - releasedAmount;
            }
            unchecked {
                return vestedAmount - releasedAmount;
            }
        }

        return 0;
    }

    /// @notice Computes the vested amount of tokens for a vesting schedule.
    /// @param _vestingSchedule vesting schedule to compute vested tokens for
    /// @param _time time to compute the vested amount at
    /// @return amount of release tokens
    function _computeVestedAmount(VestingSchedulesV2.VestingSchedule memory _vestingSchedule, uint256 _time)
        internal
        pure
        returns (uint256)
    {
        if (_time < _vestingSchedule.start + _vestingSchedule.cliffDuration) {
            // pre-cliff no tokens have been vested
            return 0;
        } else if (_time >= _vestingSchedule.start + _vestingSchedule.duration) {
            // post vesting all tokens have been vested
            return _vestingSchedule.amount;
        } else {
            uint256 timeFromStart = _time - _vestingSchedule.start;

            // compute tokens vested for completely elapsed periods
            uint256 vestedDuration = timeFromStart - timeFromStart % _vestingSchedule.periodDuration;

            return (vestedDuration * _vestingSchedule.amount) / _vestingSchedule.duration;
        }
    }

    /// @notice Computes the unlocked amount of tokens for a vesting schedule according to the global unlock schedule
    /// @param scheduledAmount amount of tokens scheduled for the vesting schedule
    /// @param timeSinceLocalLockEnd time since the local lock end
    /// @return amount of unlocked tokens
    function _computeGlobalUnlocked(uint256 scheduledAmount, uint256 timeSinceLocalLockEnd)
        internal
        pure
        returns (uint256)
    {
        // 1/24 th of the amount per month
        uint256 unlockedAmount = (scheduledAmount / 24) * (timeSinceLocalLockEnd / (365 days / 12));
        if (unlockedAmount > scheduledAmount) {
            return scheduledAmount;
        }
        return unlockedAmount;
    }

    /// @notice Returns current time
    /// @return The current time
    function _getCurrentTime() internal view virtual returns (uint256) {
        return block.timestamp;
    }
}

File 92 of 116 : ERC20VotesUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Votes.sol)

pragma solidity ^0.8.0;

import "./draft-ERC20PermitUpgradeable.sol";
import "../../../utils/math/MathUpgradeable.sol";
import "../../../governance/utils/IVotesUpgradeable.sol";
import "../../../utils/math/SafeCastUpgradeable.sol";
import "../../../utils/cryptography/ECDSAUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
 * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
 *
 * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
 *
 * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
 * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
 * power can be queried through the public accessors {getVotes} and {getPastVotes}.
 *
 * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
 * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
 *
 * _Available since v4.2._
 */
abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC20PermitUpgradeable {
    function __ERC20Votes_init() internal onlyInitializing {
    }

    function __ERC20Votes_init_unchained() internal onlyInitializing {
    }
    struct Checkpoint {
        uint32 fromBlock;
        uint224 votes;
    }

    bytes32 private constant _DELEGATION_TYPEHASH =
        keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

    mapping(address => address) private _delegates;
    mapping(address => Checkpoint[]) private _checkpoints;
    Checkpoint[] private _totalSupplyCheckpoints;

    /**
     * @dev Get the `pos`-th checkpoint for `account`.
     */
    function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
        return _checkpoints[account][pos];
    }

    /**
     * @dev Get number of checkpoints for `account`.
     */
    function numCheckpoints(address account) public view virtual returns (uint32) {
        return SafeCastUpgradeable.toUint32(_checkpoints[account].length);
    }

    /**
     * @dev Get the address `account` is currently delegating to.
     */
    function delegates(address account) public view virtual override returns (address) {
        return _delegates[account];
    }

    /**
     * @dev Gets the current votes balance for `account`
     */
    function getVotes(address account) public view virtual override returns (uint256) {
        uint256 pos = _checkpoints[account].length;
        return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
    }

    /**
     * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
     *
     * Requirements:
     *
     * - `blockNumber` must have been already mined
     */
    function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
        return _checkpointsLookup(_checkpoints[account], blockNumber);
    }

    /**
     * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
     * It is but NOT the sum of all the delegated votes!
     *
     * Requirements:
     *
     * - `blockNumber` must have been already mined
     */
    function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
        return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
    }

    /**
     * @dev Lookup a value in a list of (sorted) checkpoints.
     */
    function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
        // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
        //
        // Initially we check if the block is recent to narrow the search range.
        // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
        // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
        // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
        // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
        // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
        // out of bounds (in which case we're looking too far in the past and the result is 0).
        // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
        // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
        // the same.
        uint256 length = ckpts.length;

        uint256 low = 0;
        uint256 high = length;

        if (length > 5) {
            uint256 mid = length - MathUpgradeable.sqrt(length);
            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        while (low < high) {
            uint256 mid = MathUpgradeable.average(low, high);
            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes;
    }

    /**
     * @dev Delegate votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) public virtual override {
        _delegate(_msgSender(), delegatee);
    }

    /**
     * @dev Delegates votes from signer to `delegatee`
     */
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= expiry, "ERC20Votes: signature expired");
        address signer = ECDSAUpgradeable.recover(
            _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
            v,
            r,
            s
        );
        require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
        _delegate(signer, delegatee);
    }

    /**
     * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
     */
    function _maxSupply() internal view virtual returns (uint224) {
        return type(uint224).max;
    }

    /**
     * @dev Snapshots the totalSupply after it has been increased.
     */
    function _mint(address account, uint256 amount) internal virtual override {
        super._mint(account, amount);
        require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");

        _writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
    }

    /**
     * @dev Snapshots the totalSupply after it has been decreased.
     */
    function _burn(address account, uint256 amount) internal virtual override {
        super._burn(account, amount);

        _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
    }

    /**
     * @dev Move voting power when tokens are transferred.
     *
     * Emits a {DelegateVotesChanged} event.
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._afterTokenTransfer(from, to, amount);

        _moveVotingPower(delegates(from), delegates(to), amount);
    }

    /**
     * @dev Change delegation for `delegator` to `delegatee`.
     *
     * Emits events {DelegateChanged} and {DelegateVotesChanged}.
     */
    function _delegate(address delegator, address delegatee) internal virtual {
        address currentDelegate = delegates(delegator);
        uint256 delegatorBalance = balanceOf(delegator);
        _delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
    }

    function _moveVotingPower(
        address src,
        address dst,
        uint256 amount
    ) private {
        if (src != dst && amount > 0) {
            if (src != address(0)) {
                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
                emit DelegateVotesChanged(src, oldWeight, newWeight);
            }

            if (dst != address(0)) {
                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
                emit DelegateVotesChanged(dst, oldWeight, newWeight);
            }
        }
    }

    function _writeCheckpoint(
        Checkpoint[] storage ckpts,
        function(uint256, uint256) view returns (uint256) op,
        uint256 delta
    ) private returns (uint256 oldWeight, uint256 newWeight) {
        uint256 pos = ckpts.length;

        Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1);

        oldWeight = oldCkpt.votes;
        newWeight = op(oldWeight, delta);

        if (pos > 0 && oldCkpt.fromBlock == block.number) {
            _unsafeAccess(ckpts, pos - 1).votes = SafeCastUpgradeable.toUint224(newWeight);
        } else {
            ckpts.push(Checkpoint({fromBlock: SafeCastUpgradeable.toUint32(block.number), votes: SafeCastUpgradeable.toUint224(newWeight)}));
        }
    }

    function _add(uint256 a, uint256 b) private pure returns (uint256) {
        return a + b;
    }

    function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
        return a - b;
    }

    function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) {
        assembly {
            mstore(0, ckpts.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[47] private __gap;
}

File 93 of 116 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initialized`
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initializing`
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 94 of 116 : IERC20VestableVotesUpgradeable.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../../state/tlc/VestingSchedules.2.sol";

/// @title ERC20 Vestable Votes Upgradeable Interface(v1)
/// @author Alluvial
/// @notice This interface exposes methods to manage vestings
interface IERC20VestableVotesUpgradeableV1 {
    /// @notice A new vesting schedule has been created
    /// @param index Vesting schedule index
    /// @param creator Creator of the vesting schedule
    /// @param beneficiary Vesting beneficiary address
    /// @param amount Vesting schedule amount
    event CreatedVestingSchedule(uint256 index, address indexed creator, address indexed beneficiary, uint256 amount);

    /// @notice Vesting schedule has been released
    /// @param index Vesting schedule index
    /// @param releasedAmount Amount of tokens released to the beneficiary
    event ReleasedVestingSchedule(uint256 index, uint256 releasedAmount);

    /// @notice Vesting schedule has been revoked
    /// @param index Vesting schedule index
    /// @param returnedAmount Amount of tokens returned to the creator
    /// @param newEnd New end timestamp after revoke action
    event RevokedVestingSchedule(uint256 index, uint256 returnedAmount, uint256 newEnd);

    /// @notice Vesting escrow has been delegated
    /// @param index Vesting schedule index
    /// @param oldDelegatee old delegatee
    /// @param newDelegatee new delegatee
    /// @param beneficiary vesting schedule beneficiary
    event DelegatedVestingEscrow(
        uint256 index, address indexed oldDelegatee, address indexed newDelegatee, address indexed beneficiary
    );

    /// @notice Vesting schedule creator has unsufficient balance to create vesting schedule
    error UnsufficientVestingScheduleCreatorBalance();

    /// @notice Invalid parameter for a vesting schedule
    error InvalidVestingScheduleParameter(string msg);

    /// @notice Attempt to revoke a schedule in the past
    error VestingScheduleNotRevocableInPast();

    /// @notice The vesting schedule is not revocable
    error VestingScheduleNotRevocable();

    /// @notice The vesting schedule is locked
    error VestingScheduleIsLocked();

    /// @notice Attempt to revoke a vesting schedule with an invalid end parameter
    error InvalidRevokedVestingScheduleEnd();

    /// @notice No token to release
    error ZeroReleasableAmount();

    /// @notice Underflow in global unlock logic (should never happen)
    error GlobalUnlockUnderlfow();

    /// @notice Get vesting schedule
    /// @dev The vesting schedule structure represents a static configuration used to compute the desired
    /// @dev vesting details of a beneficiary at all times. The values won't change even after tokens are released.
    /// @dev The only dynamic field of the structure is end, and is updated whenever a vesting schedule is revoked
    /// @param _index Index of the vesting schedule
    function getVestingSchedule(uint256 _index) external view returns (VestingSchedulesV2.VestingSchedule memory);

    /// @notice Get vesting global unlock schedule activation status for a vesting schedule
    /// @param _index Index of the vesting schedule
    /// @return true if the vesting schedule should ignore the global unlock schedule
    function isGlobalUnlockedScheduleIgnored(uint256 _index) external view returns (bool);

    /// @notice Get count of vesting schedules
    /// @return count of vesting schedules
    function getVestingScheduleCount() external view returns (uint256);

    /// @notice Get the address of the escrow for a vesting schedule
    /// @param _index Index of the vesting schedule
    /// @return address of the escrow
    function vestingEscrow(uint256 _index) external view returns (address);

    /// @notice Computes the releasable amount of tokens for a vesting schedule.
    /// @param _index index of the vesting schedule
    /// @return amount of releasable tokens
    function computeVestingReleasableAmount(uint256 _index) external view returns (uint256);

    /// @notice Computes the vested amount of tokens for a vesting schedule.
    /// @param _index index of the vesting schedule
    /// @return amount of vested tokens
    function computeVestingVestedAmount(uint256 _index) external view returns (uint256);

    /// @notice Creates a new vesting schedule
    /// @notice There may delay between the time a user should start vesting tokens and the time the vesting schedule is actually created on the contract.
    /// @notice Typically a user joins the Liquid Collective but some weeks pass before the user gets all legal agreements in place and signed for the
    /// @notice token grant emission to happen. In this case, the vesting schedule created for the token grant would start on the join date which is in the past.
    /// @dev As vesting schedules can be created in the past, this means that you should be careful when creating a vesting schedule and what duration parameters
    /// @dev you use as this contract would allow creating a vesting schedule in the past and even a vesting schedule that has already ended.
    /// @param _start start time of the vesting
    /// @param _cliffDuration duration to vesting cliff (in seconds)
    /// @param _duration total vesting schedule duration after which all tokens are vested (in seconds)
    /// @param _periodDuration duration of a period after which new tokens unlock (in seconds)
    /// @param _lockDuration duration during which tokens are locked (in seconds)
    /// @param _revocable whether the vesting schedule is revocable or not
    /// @param _amount amount of token attributed by the vesting schedule
    /// @param _beneficiary address of the beneficiary of the tokens
    /// @param _delegatee address to delegate escrow voting power to
    /// @param _ignoreGlobalUnlockSchedule whether the vesting schedule should ignore the global lock
    /// @return index of the created vesting schedule
    function createVestingSchedule(
        uint64 _start,
        uint32 _cliffDuration,
        uint32 _duration,
        uint32 _periodDuration,
        uint32 _lockDuration,
        bool _revocable,
        uint256 _amount,
        address _beneficiary,
        address _delegatee,
        bool _ignoreGlobalUnlockSchedule
    ) external returns (uint256);

    /// @notice Revoke vesting schedule
    /// @param _index Index of the vesting schedule to revoke
    /// @param _end End date for the schedule
    /// @return returnedAmount amount returned to the vesting schedule creator
    function revokeVestingSchedule(uint256 _index, uint64 _end) external returns (uint256 returnedAmount);

    /// @notice Release vesting schedule
    /// @notice When tokens are released from the escrow, the delegated address of the escrow will see its voting power decrease.
    /// @notice The beneficiary has to make sure its delegation parameters are set properly to be able to use/delegate the voting power of its balance.
    /// @param _index Index of the vesting schedule to release
    /// @return released amount
    function releaseVestingSchedule(uint256 _index) external returns (uint256);

    /// @notice Delegate vesting escrowed tokens
    /// @param _index index of the vesting schedule
    /// @param _delegatee address to delegate the token to
    /// @return True on success
    function delegateVestingEscrow(uint256 _index, address _delegatee) external returns (bool);
}

File 95 of 116 : VestingSchedules.2.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./VestingSchedules.1.sol";

/// @title VestingSchedulesV2 Storage
/// @notice Utility to manage VestingSchedulesV2 in storage
library VestingSchedulesV2 {
    /// @notice Storage slot of the Vesting Schedules (note the slot is different from v1)
    bytes32 internal constant VESTING_SCHEDULES_SLOT =
        bytes32(uint256(keccak256("erc20VestableVotes.state.v2.schedules")) - 1);

    struct VestingSchedule {
        // start time of the vesting period
        uint64 start;
        // date at which the vesting is ended
        // initially it is equal to start+duration then to revoke date in case of revoke
        uint64 end;
        // duration before which first tokens gets ownable
        uint32 cliffDuration;
        // duration before tokens gets unlocked. can exceed the duration of the vesting chedule
        uint32 lockDuration;
        // duration of the entire vesting (sum of all vesting period durations)
        uint32 duration;
        // duration of a single period of vesting
        uint32 periodDuration;
        // amount of tokens granted by the vesting schedule
        uint256 amount;
        // creator of the token vesting
        address creator;
        // beneficiary of tokens after they are releaseVestingScheduled
        address beneficiary;
        // whether the schedule can be revoked
        bool revocable;
        // amount of released tokens
        uint256 releasedAmount;
    }

    /// @notice The structure at the storage slot
    struct SlotVestingSchedule {
        /// @custom:attribute Array containing all the vesting schedules
        VestingSchedule[] value;
    }

    /// @notice The VestingSchedule was not found
    /// @param index vesting schedule index
    error VestingScheduleNotFound(uint256 index);

    /// @notice Retrieve the vesting schedule in storage
    /// @param _index index of the vesting schedule
    /// @return the vesting schedule
    function get(uint256 _index) internal view returns (VestingSchedule storage) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        if (r.value.length <= _index) {
            revert VestingScheduleNotFound(_index);
        }

        return r.value[_index];
    }

    /// @notice Get vesting schedule count in storage
    /// @return The count of vesting schedule in storage
    function getCount() internal view returns (uint256) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        return r.value.length;
    }

    /// @notice Add a new vesting schedule in storage
    /// @param _newSchedule new vesting schedule to create
    /// @return The size of the vesting schedule array after the operation
    function push(VestingSchedule memory _newSchedule) internal returns (uint256) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        r.value.push(_newSchedule);

        return r.value.length;
    }

    /// @notice Migrate a VestingSchedule from v1 to v2
    /// @notice Takes a VestingSchedule in v1 format in stores it in v2 format
    /// @param _index of the schedule in v1 to be migrated
    /// @param _releasedAmount The released amount to keep in storage
    /// @return The index of the created schedule in v2 format
    function migrateVestingScheduleFromV1(uint256 _index, uint256 _releasedAmount) internal returns (uint256) {
        VestingSchedulesV1.VestingSchedule memory scheduleV1 = VestingSchedulesV1.get(_index);
        VestingSchedulesV2.VestingSchedule memory scheduleV2 = VestingSchedulesV2.VestingSchedule({
            start: scheduleV1.start,
            end: scheduleV1.end,
            lockDuration: scheduleV1.lockDuration,
            cliffDuration: scheduleV1.cliffDuration,
            duration: scheduleV1.duration,
            periodDuration: scheduleV1.periodDuration,
            amount: scheduleV1.amount,
            creator: scheduleV1.creator,
            beneficiary: scheduleV1.beneficiary,
            revocable: scheduleV1.revocable,
            releasedAmount: _releasedAmount
        });

        return push(scheduleV2) - 1;
    }
}

File 96 of 116 : IgnoreGlobalUnlockSchedule.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Global unlock schedule activation storage
/// @notice Utility to manage the global unlock schedule activation mapping in storage
/// @notice The global unlock schedule releases 1/24th of the total scheduled amount every month after the local lock end
library IgnoreGlobalUnlockSchedule {
    /// @notice Storage slot of the global unlock schedule activation mapping
    bytes32 internal constant GLOBAL_UNLOCK_ACTIVATION_SLOT =
        bytes32(uint256(keccak256("tlc.state.globalUnlockScheduleActivation")) - 1);

    /// @notice Structure stored in storage slot
    struct Slot {
        /// @custom:attribute Mapping keeping track of activation per schedule
        mapping(uint256 => bool) value;
    }

    /// @notice Retrieve the global unlock schedule activation value of a schedule, true if the global lock should be ignored
    /// @param _scheduleId The schedule id
    /// @return The global unlock activation value
    function get(uint256 _scheduleId) internal view returns (bool) {
        bytes32 slot = GLOBAL_UNLOCK_ACTIVATION_SLOT;

        Slot storage r;

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

        return r.value[_scheduleId];
    }

    /// @notice Sets the global unlock schedule activation value of a schedule
    /// @param _scheduleId The id of the schedule to modify
    /// @param _ignoreGlobalUnlock The value to set, true if the global lock should be ignored
    function set(uint256 _scheduleId, bool _ignoreGlobalUnlock) internal {
        bytes32 slot = GLOBAL_UNLOCK_ACTIVATION_SLOT;

        Slot storage r;

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

        r.value[_scheduleId] = _ignoreGlobalUnlock;
    }
}

File 97 of 116 : draft-ERC20PermitUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/draft-ERC20Permit.sol)

pragma solidity ^0.8.0;

import "./draft-IERC20PermitUpgradeable.sol";
import "../ERC20Upgradeable.sol";
import "../../../utils/cryptography/ECDSAUpgradeable.sol";
import "../../../utils/cryptography/EIP712Upgradeable.sol";
import "../../../utils/CountersUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 *
 * @custom:storage-size 51
 */
abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable {
    using CountersUpgradeable for CountersUpgradeable.Counter;

    mapping(address => CountersUpgradeable.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    /**
     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
     * However, to ensure consistency with the upgradeable transpiler, we will continue
     * to reserve a slot.
     * @custom:oz-renamed-from _PERMIT_TYPEHASH
     */
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    function __ERC20Permit_init(string memory name) internal onlyInitializing {
        __EIP712_init_unchained(name, "1");
    }

    function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSAUpgradeable.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        CountersUpgradeable.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 98 of 116 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 99 of 116 : IVotesUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 *
 * _Available since v4.5._
 */
interface IVotesUpgradeable {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the current amount of votes that `account` has.
     */
    function getVotes(address account) external view returns (uint256);

    /**
     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
     */
    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the delegate that `account` has chosen.
     */
    function delegates(address account) external view returns (address);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) external;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

File 100 of 116 : SafeCastUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 101 of 116 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 102 of 116 : draft-IERC20PermitUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 103 of 116 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}

File 104 of 116 : EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./ECDSAUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 *
 * @custom:storage-size 52
 */
abstract contract EIP712Upgradeable is Initializable {
    /* solhint-disable var-name-mixedcase */
    bytes32 private _HASHED_NAME;
    bytes32 private _HASHED_VERSION;
    bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712NameHash() internal virtual view returns (bytes32) {
        return _HASHED_NAME;
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712VersionHash() internal virtual view returns (bytes32) {
        return _HASHED_VERSION;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 105 of 116 : CountersUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library CountersUpgradeable {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 106 of 116 : IERC20Upgradeable.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 IERC20Upgradeable {
    /**
     * @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);
}

File 107 of 116 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 108 of 116 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 109 of 116 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 110 of 116 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 111 of 116 : VestingSchedules.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title VestingSchedulesV1 Storage
/// @notice Utility to manage VestingSchedulesV1 in storage
library VestingSchedulesV1 {
    /// @notice Storage slot of the Vesting Schedules
    bytes32 internal constant VESTING_SCHEDULES_SLOT =
        bytes32(uint256(keccak256("erc20VestableVotes.state.schedules")) - 1);

    struct VestingSchedule {
        // start time of the vesting period
        uint64 start;
        // date at which the vesting is ended
        // initially it is equal to start+duration then to revoke date in case of revoke
        uint64 end;
        // duration before which first tokens gets ownable
        uint32 cliffDuration;
        // duration before tokens gets unlocked. can exceed the duration of the vesting chedule
        uint32 lockDuration;
        // duration of the entire vesting (sum of all vesting period durations)
        uint32 duration;
        // duration of a single period of vesting
        uint32 periodDuration;
        // amount of tokens granted by the vesting schedule
        uint256 amount;
        // creator of the token vesting
        address creator;
        // beneficiary of tokens after they are releaseVestingScheduled
        address beneficiary;
        // whether the schedule can be revoked
        bool revocable;
    }

    /// @notice The structure at the storage slot
    struct SlotVestingSchedule {
        /// @custom:attribute Array containing all the vesting schedules
        VestingSchedule[] value;
    }

    /// @notice The VestingSchedule was not found
    /// @param index vesting schedule index
    error VestingScheduleNotFound(uint256 index);

    /// @notice Retrieve the vesting schedule in storage
    /// @param _index index of the vesting schedule
    /// @return the vesting schedule
    function get(uint256 _index) internal view returns (VestingSchedule storage) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        if (r.value.length <= _index) {
            revert VestingScheduleNotFound(_index);
        }

        return r.value[_index];
    }

    /// @notice Get vesting schedule count in storage
    /// @return The count of vesting schedule in storage
    function getCount() internal view returns (uint256) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        return r.value.length;
    }

    /// @notice Add a new vesting schedule in storage
    /// @param _newSchedule new vesting schedule to create
    /// @return The size of the vesting schedule array after the operation
    function push(VestingSchedule memory _newSchedule) internal returns (uint256) {
        bytes32 slot = VESTING_SCHEDULES_SLOT;

        SlotVestingSchedule storage r;

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

        r.value.push(_newSchedule);

        return r.value.length;
    }
}

File 112 of 116 : TLC.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./components/ERC20VestableVotesUpgradeable.1.sol";
import "./interfaces/ITLC.1.sol";

/// @title TLC (v1)
/// @author Alluvial
/// @notice The TLC token has a max supply of 1,000,000,000 and 18 decimal places.
/// @notice Upon deployment, all minted tokens are send to account provided at construction, in charge of creating the vesting schedules
/// @notice The contract is based on ERC20Votes by OpenZeppelin. Users need to delegate their voting power to someone or themselves to be able to vote.
/// @notice The contract contains vesting logics allowing vested users to still be able to delegate their voting power while their tokens are held in an escrow
contract TLCV1 is ERC20VestableVotesUpgradeableV1, ITLCV1 {
    // Token information
    string internal constant NAME = "Liquid Collective";
    string internal constant SYMBOL = "TLC";

    // Initial supply of token minted
    uint256 internal constant INITIAL_SUPPLY = 1_000_000_000e18; // 1 billion TLC

    /// @notice Disables implementation initialization
    constructor() {
        _disableInitializers();
    }

    /// @inheritdoc ITLCV1
    function initTLCV1(address _account) external initializer {
        LibSanitize._notZeroAddress(_account);
        __ERC20Permit_init(NAME);
        __ERC20_init(NAME, SYMBOL);
        _mint(_account, INITIAL_SUPPLY);
    }

    /// @inheritdoc ITLCV1
    function migrateVestingSchedules() external reinitializer(2) {
        ERC20VestableVotesUpgradeableV1.migrateVestingSchedulesFromV1ToV2();
    }
}

File 113 of 116 : ITLC.1.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol";
import "openzeppelin-contracts-upgradeable/contracts/governance/utils/IVotesUpgradeable.sol";

import "./components/IERC20VestableVotesUpgradeable.1.sol";

/// @title TLC Interface (v1)
/// @author Alluvial
/// @notice TLC token interface
interface ITLCV1 is IERC20VestableVotesUpgradeableV1, IVotesUpgradeable, IERC20Upgradeable {
    /// @notice Initializes the TLC Token
    /// @param _account The initial account to grant all the minted tokens
    function initTLCV1(address _account) external;

    /// @notice Migrates the vesting schedule state structures
    function migrateVestingSchedules() external;
}

File 114 of 116 : TLC_globalUnlockScheduleMigration.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "../state/tlc/VestingSchedules.2.sol";
import "../state/tlc/IgnoreGlobalUnlockSchedule.sol";

struct VestingScheduleMigration {
    // number of consecutive schedules to migrate with the same parameters
    uint8 scheduleCount;
    // The new lock duration
    uint32 newLockDuration;
    // if != 0, the new start value
    uint64 newStart;
    // if != 0, the new end value
    uint64 newEnd;
    // set cliff to 0 if true
    bool setCliff;
    // if true set vesting duration to 86400
    bool setDuration;
    // if true set vesting period duration to 86400
    bool setPeriodDuration;
    // if true schedule will not be subject to global unlock schedule
    bool ignoreGlobalUnlock;
}

uint256 constant OCTOBER_16_2024 = 1729036800;

contract TlcMigration {
    error CliffTooLong(uint256 i);
    error WrongUnlockDate(uint256 i);
    error WrongEnd(uint256 i);

    function migrate() external {
        VestingScheduleMigration[] memory migrations = new VestingScheduleMigration[](30);
        // 0 -> 6
        migrations[0] = VestingScheduleMigration({
            scheduleCount: 7,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 75772800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 7
        migrations[1] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 70329600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 8
        migrations[2] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 65491200,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 9 -> 12
        migrations[3] = VestingScheduleMigration({
            scheduleCount: 4,
            newStart: 0,
            newEnd: 1656720000,
            newLockDuration: 72403200,
            setCliff: true,
            setDuration: true,
            setPeriodDuration: true,
            ignoreGlobalUnlock: false
        });
        // 13
        migrations[4] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 67046400,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 14
        migrations[5] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 56505600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 15
        migrations[6] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 58233600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 16
        migrations[7] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 57974400,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 17
        migrations[8] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 53740800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 18
        migrations[9] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 75772800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 19
        migrations[10] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 49474800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 20
        migrations[11] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 75772800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 21
        migrations[12] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 49474800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 22
        migrations[13] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 75772800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 23
        migrations[14] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 49474800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 24 -> 26
        migrations[15] = VestingScheduleMigration({
            scheduleCount: 3,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 75772800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 27
        migrations[16] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 70329600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 28 -> 29
        migrations[17] = VestingScheduleMigration({
            scheduleCount: 2,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50371200,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 30
        migrations[18] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50716800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 31
        migrations[19] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50803200,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 32
        migrations[20] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50889600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 33
        migrations[21] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50716800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 34 -> 35
        migrations[22] = VestingScheduleMigration({
            scheduleCount: 2,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 50889600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 36 -> 60
        migrations[23] = VestingScheduleMigration({
            scheduleCount: 25,
            newStart: 1686175200,
            newEnd: 1686261600,
            newLockDuration: 42861600,
            setCliff: false,
            setDuration: true,
            setPeriodDuration: true,
            ignoreGlobalUnlock: false
        });
        // 61
        migrations[24] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 40953600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 62
        migrations[25] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 48729600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: false
        });
        // 63
        migrations[26] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 41644800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 64
        migrations[27] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 47001600,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 65
        migrations[28] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 45014400,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // 66
        migrations[29] = VestingScheduleMigration({
            scheduleCount: 1,
            newStart: 0,
            newEnd: 0,
            newLockDuration: 38188800,
            setCliff: false,
            setDuration: false,
            setPeriodDuration: false,
            ignoreGlobalUnlock: true
        });
        // All schedules covered

        uint256 index = 0;
        for (uint256 i = 0; i < migrations.length; i++) {
            VestingScheduleMigration memory migration = migrations[i];
            for (uint256 j = 0; j < migration.scheduleCount; j++) {
                VestingSchedulesV2.VestingSchedule storage sch = VestingSchedulesV2.get(index);

                bool isRevoked = false;
                if (sch.start + sch.duration != sch.end) {
                    isRevoked = true;
                }
                // Modifications
                sch.lockDuration = migration.newLockDuration;
                if (migration.newStart != 0) {
                    sch.start = migration.newStart;
                }
                if (migration.newEnd != 0) {
                    sch.end = migration.newEnd;
                }
                if (migration.setCliff) {
                    sch.cliffDuration = 0;
                }
                if (migration.setDuration) {
                    sch.duration = 86400;
                }
                if (migration.setPeriodDuration) {
                    sch.periodDuration = 86400;
                }
                if (migration.ignoreGlobalUnlock) {
                    IgnoreGlobalUnlockSchedule.set(index, true);
                }

                // Post effects checks
                // check cliff is not longer than duration
                if (sch.cliffDuration > sch.duration) {
                    revert CliffTooLong(index);
                }
                // sanity checks on non revoked schedules
                if (!isRevoked && (sch.end != sch.start + sch.duration)) {
                    revert WrongEnd(index);
                }
                // check all the schedules are locked until unix : 1729036800
                if (sch.start + sch.lockDuration != OCTOBER_16_2024) {
                    revert WrongUnlockDate(index);
                }

                index += 1;
            }
        }
    }
}

File 115 of 116 : Firewall.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

import "./interfaces/IFirewall.sol";

import "./Administrable.sol";

/// @title Firewall
/// @author Figment
/// @notice This contract accepts calls to admin-level functions of an underlying contract, and
///         ensures the caller holds an appropriate role for calling that function. There are two roles:
///          - An Admin can call anything
///          - An Executor can call specific functions. The list of function is customisable.
///         Random callers cannot call anything through this contract, even if the underlying function
///         is unpermissioned in the underlying contract.
///         Calls to non-admin functions should be called at the underlying contract directly.
contract Firewall is IFirewall, Administrable {
    /// @inheritdoc IFirewall
    address public executor;

    /// @inheritdoc IFirewall
    address public destination;

    /// @inheritdoc IFirewall
    mapping(bytes4 => bool) public executorCanCall;

    /// @param _admin Address of the administrator, that is able to perform all calls via the Firewall
    /// @param _executor Address of the executor, that is able to perform only a subset of calls via the Firewall
    /// @param _executorCallableSelectors Initial list of allowed selectors for the executor
    constructor(address _admin, address _executor, address _destination, bytes4[] memory _executorCallableSelectors) {
        LibSanitize._notZeroAddress(_executor);
        LibSanitize._notZeroAddress(_destination);
        _setAdmin(_admin);
        executor = _executor;
        destination = _destination;

        emit SetExecutor(_executor);
        emit SetDestination(_destination);

        for (uint256 i; i < _executorCallableSelectors.length;) {
            executorCanCall[_executorCallableSelectors[i]] = true;
            emit SetExecutorPermissions(_executorCallableSelectors[i], true);
            unchecked {
                ++i;
            }
        }
    }

    /// @notice Prevents unauthorized calls
    modifier onlyAdminOrExecutor() {
        if (_getAdmin() != msg.sender && msg.sender != executor) {
            revert LibErrors.Unauthorized(msg.sender);
        }
        _;
    }

    /// @inheritdoc IFirewall
    function setExecutor(address _newExecutor) external onlyAdminOrExecutor {
        LibSanitize._notZeroAddress(_newExecutor);
        executor = _newExecutor;
        emit SetExecutor(_newExecutor);
    }

    /// @inheritdoc IFirewall
    function allowExecutor(bytes4 _functionSelector, bool _executorCanCall) external onlyAdmin {
        executorCanCall[_functionSelector] = _executorCanCall;
        emit SetExecutorPermissions(_functionSelector, _executorCanCall);
    }

    /// @inheritdoc IFirewall
    fallback() external payable virtual {
        _fallback();
    }

    /// @inheritdoc IFirewall
    receive() external payable virtual {
        _fallback();
    }

    /// @notice Performs call checks to verify that the caller is able to perform the call
    function _checkCallerRole() internal view {
        if (msg.sender == _getAdmin() || (executorCanCall[msg.sig] && msg.sender == executor)) {
            return;
        }
        revert LibErrors.Unauthorized(msg.sender);
    }

    /// @notice Forwards the current call parameters to the destination address
    /// @param _destination Address on which the forwarded call is performed
    /// @param _value Message value to attach to the call
    function _forward(address _destination, uint256 _value) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the destination.
            // out and outsize are 0 because we don't know the size yet.
            let result := call(gas(), _destination, _value, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // call returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    /// @notice Internal utility to perform authorization checks and forward a call
    function _fallback() internal virtual {
        _checkCallerRole();
        _forward(destination, msg.value);
    }
}

File 116 of 116 : IFirewall.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.10;

/// @title Firewall
/// @author Figment
/// @notice This interface exposes methods to accept calls to admin-level functions of an underlying contract.
interface IFirewall {
    /// @notice The stored executor address has been changed
    /// @param executor The new executor address
    event SetExecutor(address indexed executor);

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

    /// @notice The storage permission for a selector has been changed
    /// @param selector The 4 bytes method selector
    /// @param status True if executor is allowed
    event SetExecutorPermissions(bytes4 selector, bool status);

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

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

    /// @notice Returns true if the executor is allowed to perform a call on the given selector
    /// @param _selector The selector to verify
    /// @return True if executor is allowed to call
    function executorCanCall(bytes4 _selector) external view returns (bool);

    /// @notice Sets the executor address
    /// @param _newExecutor New address for the executor
    function setExecutor(address _newExecutor) external;

    /// @notice Sets the permission for a function selector
    /// @param _functionSelector Method signature on which the permission is changed
    /// @param _executorCanCall True if selector is callable by the executor
    function allowExecutor(bytes4 _functionSelector, bool _executorCanCall) external;

    /// @notice Fallback method. All its parameters are forwarded to the destination if caller is authorized
    fallback() external payable;

    /// @notice Receive fallback method. All its parameters are forwarded to the destination if caller is authorized
    receive() external payable;
}

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

Contract Security Audit

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"}]

60806040523480156200001157600080fd5b506200002a6000196200007060201b62000a931760201c565b7f1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a716000196000366040516200006293929190620000bd565b60405180910390a162000119565b620000b6620000a160017f82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfd620000f3565b60001b82620000b960201b62000ab71760201c565b50565b9055565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b6000828210156200011457634e487b7160e01b600052601160045260246000fd5b500390565b61215280620001296000396000f3fe6080604052600436106100b85760003560e01c80630c779401146100bd5780630d8d2a54146100df578063107703ab14610107578063319798d1146101345780635b5985a2146101495780635f2e5f071461015e5780637c044e551461018b578063841ecb85146101b857806386233754146101cd5780639332525d1461020f5780639b92d6de1461022f578063a519f0601461028b578063aa2f892d146102ad578063b30d8bb7146102cd578063c8fade5a146102e0575b600080fd5b3480156100c957600080fd5b506100dd6100d8366004611a92565b610300565b005b3480156100eb57600080fd5b506100f46103da565b6040519081526020015b60405180910390f35b34801561011357600080fd5b50610127610122366004611ac0565b6103e9565b6040516100fe9190611af0565b34801561014057600080fd5b506100f46104db565b34801561015557600080fd5b506100f46104eb565b34801561016a57600080fd5b5061017e610179366004611b4c565b6104f5565b6040516100fe9190611b8d565b34801561019757600080fd5b506101ab6101a6366004611be2565b61062a565b6040516100fe9190611c7d565b3480156101c457600080fd5b506100f4610645565b3480156101d957600080fd5b506101ed6101e8366004611cb8565b61064f565b60408051825181526020808401519082015291810151908201526060016100fe565b34801561021b57600080fd5b506101ab61022a366004611ce5565b6106b4565b34801561023b57600080fd5b5061024f61024a366004611cb8565b6106d0565b6040516100fe919081518152602080830151908201526040808301516001600160a01b0316908201526060918201519181019190915260800190565b34801561029757600080fd5b506102a0610747565b6040516100fe9190611d50565b3480156102b957600080fd5b506101276102c8366004611a92565b610751565b6100dd6102db366004611a92565b610829565b3480156102ec57600080fd5b506100dd6102fb366004611d64565b6109c5565b610308610abb565b6001600160a01b0316336001600160a01b031614610344573360405163472511eb60e11b815260040161033b9190611d50565b60405180910390fd5b6000610357610351610add565b83610afb565b905080156103d65761037a8161036b610add565b6103759190611d97565b610b10565b610382610747565b6001600160a01b031663056850c6826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156103bc57600080fd5b505af11580156103d0573d6000803e3d6000fd5b50505050505b5050565b60006103e4610b2c565b905090565b6000806103f4610747565b9050336001600160a01b038216146104c757806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610444573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104689190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b8152600401610496929190611dcb565b60006040518083038186803b1580156104ae57600080fd5b505afa1580156104c2573d6000803e3d6000fd5b505050505b506104d28383610b4a565b90505b92915050565b60006104e5610e08565b54919050565b60006103e4610add565b6060816001600160401b0381111561050f5761050f611de4565b604051908082528060200260200182016040528015610538578160200160208202803683370190505b5090506105436119e9565b600061054d610e36565b805490915080156105ae5781610564600183611d97565b8154811061057457610574611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b60005b85811015610620576105e98787838181106105ce576105ce611dfa565b90506020020160208101906105e39190611cb8565b85610e64565b8582815181106105fb576105fb611dfa565b602002602001019060070b908160070b815250508061061990611e10565b90506105b1565b5050505092915050565b606061063a878787878787610f46565b979650505050505050565b60006104e5610e36565b6106576119e9565b61065f610e36565b8263ffffffff168154811061067657610676611dfa565b906000526020600020906003020160405180606001604052908160008201548152602001600182015481526020016002820154815250509050919050565b60606106c785858585600161ffff610f46565b95945050505050565b6106d8611a0a565b6106e0610e08565b8263ffffffff16815481106106f7576106f7611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101549383019390935260028301546001600160a01b031690820152600390910154606082015292915050565b60006103e4610abb565b60008061075c610747565b9050806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c09190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b81526004016107ee929190611dcb565b60006040518083038186803b15801561080657600080fd5b505afa15801561081a573d6000803e3d6000fd5b50505050506104d58233610b4a565b610831610abb565b6001600160a01b0316336001600160a01b031614610864573360405163472511eb60e11b815260040161033b9190611d50565b600061086e610b2c565b90508082111561089b57604051632ea1a50d60e11b8152600481018390526024810182905260440161033b565b60006108a5610e36565b805490915060003463ffffffff831615610926576000846108c7600186611e2b565b63ffffffff16815481106108dd576108dd611dfa565b60009182526020918290206040805160608101825260039093029091018054808452600182015494840194909452600201549082018190529092506109229190611e50565b9250505b604080516060810182528781526020808201848152928201858152875460018181018a5560008a81529390932093516003909102909301928355925190820155905160029091015561098061097b8787611d97565b6113db565b7f05e5b10ca266c2e8cfd73b1fb719694fb6b89fcda9e3739ace4cc19b3e855445828783866040516109b59493929190611e68565b60405180910390a1505050505050565b60006109cf611424565b8114610a0057806109de611424565b604051631cfd276760e31b81526004810192909252602482015260440161033b565b610a13610a0e826001611e50565b610a93565b610a1c82611442565b7ffc0d749143b04e0d92a3b49edae84a45f6bc88b75e99d9118ec4d7e87fe626c582604051610a4b9190611d50565b60405180910390a17f1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a7181600036604051610a8793929190611e89565b60405180910390a15050565b610ab4610aaf60016000805160206120fd833981519152611d97565b829055565b50565b9055565b60006103e4610ad9600160008051602061209d833981519152611d97565b5490565b60006103e4610ad960016000805160206120bd833981519152611d97565b6000818311610b0a57826104d2565b50919050565b610ab4610aaf60016000805160206120bd833981519152611d97565b60006103e4610ad960016000805160206120dd833981519152611d97565b6000610b5582611467565b82610b7357604051630dd484e760e41b815260040160405180910390fd5b610b7b610747565b6040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b0391909116906323b872dd906064016020604051808303816000875af1158015610bd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf49190611ebf565b610c11576040516313ff771f60e21b815260040160405180910390fd5b6000610c1b610e08565b805492509050600063ffffffff831615610cb257600082610c3d600186611e2b565b63ffffffff1681548110610c5357610c53611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201819052909250610cae9190611e50565b9150505b6000610cbc610747565b6001600160a01b031663f79c3f02876040518263ffffffff1660e01b8152600401610ce991815260200190565b602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190611edc565b6040805160808101825288815260208082018481526001600160a01b038a811694840194855260608401888152895460018082018c5560008c8152959095209551600490910290950194855591519284019290925592516002830180546001600160a01b0319169190921617905590516003909101559050610db886610dae610b2c565b61097b9190611e50565b846001600160a01b03167f9a1bb960783a8679f42036f0c1fe89288fd279ed545a19f86bd284c72947b18783888488604051610df79493929190611e68565b60405180910390a250505092915050565b6000806104d560017f232f0d723a47bd57c991606dd0525f28484745095475b69dffe8416b3749c3c2611d97565b6000806104d560017f0c9122afab04eaf9e9bdaa10927c74406e40c7f08744e32826fff29b1c9e7abd611d97565b600080610e6f610e08565b805490915063ffffffff851610610e8b576001199150506104d5565b6000818563ffffffff1681548110610ea557610ea5611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201529150610f0357600219925050506104d5565b610f0b610e36565b541580610f2c5750606081015184516040860151610f299190611e50565b11155b15610f3d57600019925050506104d5565b6106c78161148e565b606085848114610f6957604051633a5e3f0d60e01b815260040160405180910390fd5b806001600160401b03811115610f8157610f81611de4565b604051908082528060200260200182016040528015610faa578160200160208202803683370190505b5091506000610fb7610e08565b90506000610fc3610e36565b9050610fcd611a3b565b815463ffffffff166080820152825460005b858110156113cb578c8c82818110610ff957610ff9611dfa565b905060200201602081019061100e9190611cb8565b63ffffffff1683528a8a8281811061102857611028611dfa565b905060200201602081019061103d9190611cb8565b63ffffffff90811660408501528351838216911610611072578251604051637ba6f8f560e11b815261033b9190600401611af0565b826080015163ffffffff16836040015163ffffffff16106110ac57826040015160405163311569d960e01b815260040161033b9190611af0565b848d8d838181106110bf576110bf611dfa565b90506020020160208101906110d49190611cb8565b63ffffffff16815481106110ea576110ea611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101548385015260028101546001600160a01b0316918301919091526003015460608201529084018190525161118d57881561117157600287828151811061115757611157611dfa565b60ff90921660209283029190910190910152600101610fdf565b82516040516339775c8160e01b815261033b9190600401611af0565b838b8b838181106111a0576111a0611dfa565b90506020020160208101906111b59190611cb8565b63ffffffff16815481106111cb576111cb611dfa565b600091825260209182902060408051606080820183526003909402909201805483526001810154838601526002015490820152908501819052908401516112119161160a565b611245578251604080850151905163b826420960e01b815263ffffffff92831660048201529116602482015260440161033b565b61ffff881660a0840152600060e0840181905260c08401526112668361163a565b6020830151511561127857600161127b565b60005b87828151811061128d5761128d611dfa565b602002602001019060ff16908160ff16815250506000808460200151604001516001600160a01b03168560e0015160405160006040518083038185875af1925050503d80600081146112fb576040519150601f19603f3d011682016040523d82523d6000602084013e611300565b606091505b50915091508161132f578460200151604001518160405163f2fc9fa160e01b815260040161033b929190611ef5565b50508260200151604001516001600160a01b03168d8d8381811061135557611355611dfa565b905060200201602081019061136a9190611cb8565b60e085015160c086015160208088015151604080519485529184019290925282015263ffffffff91909116907f25f4dfa5f0703d4c509bd7216e70f8378f419433c14840c14f3eaadb60642ad19060600160405180910390a3600101610fdf565b5050505050509695505050505050565b7f81ba2e33f1e91d5b498c91742740d1de8a6149f571c26bec9ae77b5cf099c582611404610b2c565b60408051918252602082018490520160405180910390a1610ab48161190f565b60006103e4610ad960016000805160206120fd833981519152611d97565b61144b81611467565b610ab4610aaf600160008051602061209d833981519152611d97565b6001600160a01b038116610ab45760405163f6b2911f60e01b815260040160405180910390fd5b600080611499610e36565b9050600060016114a7610e36565b546114b29190611d97565b905061150f8483836001600160401b0316815481106114d3576114d3611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505061160a565b1561151b579392505050565b600061153c8584836001600160401b0316815481106114d3576114d3611dfa565b1561154957949350505050565b8160070b8160070b1461160257600060026115648484611f5a565b61156e9190611fc1565b9050600084826001600160401b03168154811061158d5761158d611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505090506115d0878261160a565b156115df575095945050505050565b8060400151876060015110156115f7578193506115fb565b8192505b5050611549565b949350505050565b8051604082015160009161161d91611e50565b83606001511080156104d257505060400151606090910151101590565b61165e60405180606001604052806000815260200160008152602001600081525090565b6060820151805160409091015160009161167791611e50565b6020840151805160609091015191925061169a916116959084611d97565b610afb565b60208084018290526060850151805191015190916116b89190611fff565b6116c2919061201e565b8252602080840151805190820151918401516000926116e091611fff565b6116ea919061201e565b8351909150811015611724578251611703908290611d97565b6040840181905261172090611716610add565b6103759190611e50565b8083525b8260200151846020015160600181815161173e9190611e50565b905250602080840151908501518051611758908390611d97565b9052508251602080860151018051611771908390611d97565b905250602083015160c08501805161178a908390611e50565b905250825160e0850180516117a0908390611e50565b90525060408481015185516020868101518751828a0151518987015187519384529383019190915294810194909452606084015263ffffffff918216929116907f01d87b0f07b182b784c074487c5a8f901a8e2348275f5480d822a70de74fa8b79060800160405180910390a35050602082015151158015906118425750816080015163ffffffff168260400151600161183a9190612032565b63ffffffff16105b8015611856575060008260a0015161ffff16115b15611906576000611865610e36565b90508260400180516118769061205a565b63ffffffff16908163ffffffff168152505080836040015163ffffffff16815481106118a4576118a4611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505083606001819052508260a00180516118f29061207e565b61ffff1690526119018361163a565b505050565b6103d68261192b565b610ab4610aaf60016000805160206120dd833981519152611d97565b6000611935610e08565b905081602001516060015181836000015163ffffffff168154811061195c5761195c611dfa565b90600052602060002090600402016003018190555081602001516000015181836000015163ffffffff168154811061199657611996611dfa565b90600052602060002090600402016000018190555081602001516020015181836000015163ffffffff16815481106119d0576119d0611dfa565b9060005260206000209060040201600101819055505050565b60405180606001604052806000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b604051806101000160405280600063ffffffff168152602001611a5c611a0a565b815260006020820152604001611a706119e9565b8152600060208201819052604082018190526060820181905260809091015290565b600060208284031215611aa457600080fd5b5035919050565b6001600160a01b0381168114610ab457600080fd5b60008060408385031215611ad357600080fd5b823591506020830135611ae581611aab565b809150509250929050565b63ffffffff91909116815260200190565b60008083601f840112611b1357600080fd5b5081356001600160401b03811115611b2a57600080fd5b6020830191508360208260051b8501011115611b4557600080fd5b9250929050565b60008060208385031215611b5f57600080fd5b82356001600160401b03811115611b7557600080fd5b611b8185828601611b01565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160070b83529284019291840191600101611ba9565b50909695505050505050565b8015158114610ab457600080fd5b60008060008060008060808789031215611bfb57600080fd5b86356001600160401b0380821115611c1257600080fd5b611c1e8a838b01611b01565b90985096506020890135915080821115611c3757600080fd5b50611c4489828a01611b01565b9095509350506040870135611c5881611bd4565b9150606087013561ffff81168114611c6f57600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160ff1683529284019291840191600101611c99565b600060208284031215611cca57600080fd5b813563ffffffff81168114611cde57600080fd5b9392505050565b60008060008060408587031215611cfb57600080fd5b84356001600160401b0380821115611d1257600080fd5b611d1e88838901611b01565b90965094506020870135915080821115611d3757600080fd5b50611d4487828801611b01565b95989497509550505050565b6001600160a01b0391909116815260200190565b600060208284031215611d7657600080fd5b8135611cde81611aab565b634e487b7160e01b600052601160045260246000fd5b600082821015611da957611da9611d81565b500390565b600060208284031215611dc057600080fd5b8151611cde81611aab565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415611e2457611e24611d81565b5060010190565b600063ffffffff83811690831681811015611e4857611e48611d81565b039392505050565b60008219821115611e6357611e63611d81565b500190565b9384526020840192909252604083015263ffffffff16606082015260800190565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b600060208284031215611ed157600080fd5b8151611cde81611bd4565b600060208284031215611eee57600080fd5b5051919050565b60018060a01b038316815260006020604081840152835180604085015260005b81811015611f3157858101830151858201606001528201611f15565b81811115611f43576000606083870101525b50601f01601f191692909201606001949350505050565b6000600782810b9084900b828212801560016001603f1b0384900383131615611f8557611f85611d81565b60016001603f1b03198390038212811615611fa257611fa2611d81565b50019392505050565b634e487b7160e01b600052601260045260246000fd5b60008160070b8360070b80611fd857611fd8611fab565b60016001603f1b0319821460001982141615611ff657611ff6611d81565b90059392505050565b600081600019048311821515161561201957612019611d81565b500290565b60008261202d5761202d611fab565b500490565b600063ffffffff80831681851680830382111561205157612051611d81565b01949350505050565b600063ffffffff8083168181141561207457612074611d81565b6001019392505050565b600061ffff82168061209257612092611d81565b600019019291505056fe1ec4138404500a2a0be2c2f9b103581c2a7fa783a934f91a6cc5cc924404973c9afb091d33aa9fdd4ac2dbd1cbba78215a097062e9dbdc7b707ac5bb59049bc18ec604dcc41d380ed48f70a4a32dd9623b98c284836361a21b8992d2845ee53d82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfda2646970667358221220217c4fd0cfc891004dde0e311f7c7606db72a075c7156051d3cbb7d569f9775664736f6c634300080a0033

Deployed Bytecode

0x6080604052600436106100b85760003560e01c80630c779401146100bd5780630d8d2a54146100df578063107703ab14610107578063319798d1146101345780635b5985a2146101495780635f2e5f071461015e5780637c044e551461018b578063841ecb85146101b857806386233754146101cd5780639332525d1461020f5780639b92d6de1461022f578063a519f0601461028b578063aa2f892d146102ad578063b30d8bb7146102cd578063c8fade5a146102e0575b600080fd5b3480156100c957600080fd5b506100dd6100d8366004611a92565b610300565b005b3480156100eb57600080fd5b506100f46103da565b6040519081526020015b60405180910390f35b34801561011357600080fd5b50610127610122366004611ac0565b6103e9565b6040516100fe9190611af0565b34801561014057600080fd5b506100f46104db565b34801561015557600080fd5b506100f46104eb565b34801561016a57600080fd5b5061017e610179366004611b4c565b6104f5565b6040516100fe9190611b8d565b34801561019757600080fd5b506101ab6101a6366004611be2565b61062a565b6040516100fe9190611c7d565b3480156101c457600080fd5b506100f4610645565b3480156101d957600080fd5b506101ed6101e8366004611cb8565b61064f565b60408051825181526020808401519082015291810151908201526060016100fe565b34801561021b57600080fd5b506101ab61022a366004611ce5565b6106b4565b34801561023b57600080fd5b5061024f61024a366004611cb8565b6106d0565b6040516100fe919081518152602080830151908201526040808301516001600160a01b0316908201526060918201519181019190915260800190565b34801561029757600080fd5b506102a0610747565b6040516100fe9190611d50565b3480156102b957600080fd5b506101276102c8366004611a92565b610751565b6100dd6102db366004611a92565b610829565b3480156102ec57600080fd5b506100dd6102fb366004611d64565b6109c5565b610308610abb565b6001600160a01b0316336001600160a01b031614610344573360405163472511eb60e11b815260040161033b9190611d50565b60405180910390fd5b6000610357610351610add565b83610afb565b905080156103d65761037a8161036b610add565b6103759190611d97565b610b10565b610382610747565b6001600160a01b031663056850c6826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156103bc57600080fd5b505af11580156103d0573d6000803e3d6000fd5b50505050505b5050565b60006103e4610b2c565b905090565b6000806103f4610747565b9050336001600160a01b038216146104c757806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610444573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104689190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b8152600401610496929190611dcb565b60006040518083038186803b1580156104ae57600080fd5b505afa1580156104c2573d6000803e3d6000fd5b505050505b506104d28383610b4a565b90505b92915050565b60006104e5610e08565b54919050565b60006103e4610add565b6060816001600160401b0381111561050f5761050f611de4565b604051908082528060200260200182016040528015610538578160200160208202803683370190505b5090506105436119e9565b600061054d610e36565b805490915080156105ae5781610564600183611d97565b8154811061057457610574611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b60005b85811015610620576105e98787838181106105ce576105ce611dfa565b90506020020160208101906105e39190611cb8565b85610e64565b8582815181106105fb576105fb611dfa565b602002602001019060070b908160070b815250508061061990611e10565b90506105b1565b5050505092915050565b606061063a878787878787610f46565b979650505050505050565b60006104e5610e36565b6106576119e9565b61065f610e36565b8263ffffffff168154811061067657610676611dfa565b906000526020600020906003020160405180606001604052908160008201548152602001600182015481526020016002820154815250509050919050565b60606106c785858585600161ffff610f46565b95945050505050565b6106d8611a0a565b6106e0610e08565b8263ffffffff16815481106106f7576106f7611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101549383019390935260028301546001600160a01b031690820152600390910154606082015292915050565b60006103e4610abb565b60008061075c610747565b9050806001600160a01b031663c5eff3d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561079c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c09190611dae565b6001600160a01b0316635a4091023360046040518363ffffffff1660e01b81526004016107ee929190611dcb565b60006040518083038186803b15801561080657600080fd5b505afa15801561081a573d6000803e3d6000fd5b50505050506104d58233610b4a565b610831610abb565b6001600160a01b0316336001600160a01b031614610864573360405163472511eb60e11b815260040161033b9190611d50565b600061086e610b2c565b90508082111561089b57604051632ea1a50d60e11b8152600481018390526024810182905260440161033b565b60006108a5610e36565b805490915060003463ffffffff831615610926576000846108c7600186611e2b565b63ffffffff16815481106108dd576108dd611dfa565b60009182526020918290206040805160608101825260039093029091018054808452600182015494840194909452600201549082018190529092506109229190611e50565b9250505b604080516060810182528781526020808201848152928201858152875460018181018a5560008a81529390932093516003909102909301928355925190820155905160029091015561098061097b8787611d97565b6113db565b7f05e5b10ca266c2e8cfd73b1fb719694fb6b89fcda9e3739ace4cc19b3e855445828783866040516109b59493929190611e68565b60405180910390a1505050505050565b60006109cf611424565b8114610a0057806109de611424565b604051631cfd276760e31b81526004810192909252602482015260440161033b565b610a13610a0e826001611e50565b610a93565b610a1c82611442565b7ffc0d749143b04e0d92a3b49edae84a45f6bc88b75e99d9118ec4d7e87fe626c582604051610a4b9190611d50565b60405180910390a17f1809e49bba43f2d39fa57894b50cd6ccb428cc438230e065cac3eb24a1355a7181600036604051610a8793929190611e89565b60405180910390a15050565b610ab4610aaf60016000805160206120fd833981519152611d97565b829055565b50565b9055565b60006103e4610ad9600160008051602061209d833981519152611d97565b5490565b60006103e4610ad960016000805160206120bd833981519152611d97565b6000818311610b0a57826104d2565b50919050565b610ab4610aaf60016000805160206120bd833981519152611d97565b60006103e4610ad960016000805160206120dd833981519152611d97565b6000610b5582611467565b82610b7357604051630dd484e760e41b815260040160405180910390fd5b610b7b610747565b6040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b0391909116906323b872dd906064016020604051808303816000875af1158015610bd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf49190611ebf565b610c11576040516313ff771f60e21b815260040160405180910390fd5b6000610c1b610e08565b805492509050600063ffffffff831615610cb257600082610c3d600186611e2b565b63ffffffff1681548110610c5357610c53611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201819052909250610cae9190611e50565b9150505b6000610cbc610747565b6001600160a01b031663f79c3f02876040518263ffffffff1660e01b8152600401610ce991815260200190565b602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190611edc565b6040805160808101825288815260208082018481526001600160a01b038a811694840194855260608401888152895460018082018c5560008c8152959095209551600490910290950194855591519284019290925592516002830180546001600160a01b0319169190921617905590516003909101559050610db886610dae610b2c565b61097b9190611e50565b846001600160a01b03167f9a1bb960783a8679f42036f0c1fe89288fd279ed545a19f86bd284c72947b18783888488604051610df79493929190611e68565b60405180910390a250505092915050565b6000806104d560017f232f0d723a47bd57c991606dd0525f28484745095475b69dffe8416b3749c3c2611d97565b6000806104d560017f0c9122afab04eaf9e9bdaa10927c74406e40c7f08744e32826fff29b1c9e7abd611d97565b600080610e6f610e08565b805490915063ffffffff851610610e8b576001199150506104d5565b6000818563ffffffff1681548110610ea557610ea5611dfa565b6000918252602091829020604080516080810182526004909302909101805480845260018201549484019490945260028101546001600160a01b0316918301919091526003015460608201529150610f0357600219925050506104d5565b610f0b610e36565b541580610f2c5750606081015184516040860151610f299190611e50565b11155b15610f3d57600019925050506104d5565b6106c78161148e565b606085848114610f6957604051633a5e3f0d60e01b815260040160405180910390fd5b806001600160401b03811115610f8157610f81611de4565b604051908082528060200260200182016040528015610faa578160200160208202803683370190505b5091506000610fb7610e08565b90506000610fc3610e36565b9050610fcd611a3b565b815463ffffffff166080820152825460005b858110156113cb578c8c82818110610ff957610ff9611dfa565b905060200201602081019061100e9190611cb8565b63ffffffff1683528a8a8281811061102857611028611dfa565b905060200201602081019061103d9190611cb8565b63ffffffff90811660408501528351838216911610611072578251604051637ba6f8f560e11b815261033b9190600401611af0565b826080015163ffffffff16836040015163ffffffff16106110ac57826040015160405163311569d960e01b815260040161033b9190611af0565b848d8d838181106110bf576110bf611dfa565b90506020020160208101906110d49190611cb8565b63ffffffff16815481106110ea576110ea611dfa565b60009182526020918290206040805160808101825260049093029091018054835260018101548385015260028101546001600160a01b0316918301919091526003015460608201529084018190525161118d57881561117157600287828151811061115757611157611dfa565b60ff90921660209283029190910190910152600101610fdf565b82516040516339775c8160e01b815261033b9190600401611af0565b838b8b838181106111a0576111a0611dfa565b90506020020160208101906111b59190611cb8565b63ffffffff16815481106111cb576111cb611dfa565b600091825260209182902060408051606080820183526003909402909201805483526001810154838601526002015490820152908501819052908401516112119161160a565b611245578251604080850151905163b826420960e01b815263ffffffff92831660048201529116602482015260440161033b565b61ffff881660a0840152600060e0840181905260c08401526112668361163a565b6020830151511561127857600161127b565b60005b87828151811061128d5761128d611dfa565b602002602001019060ff16908160ff16815250506000808460200151604001516001600160a01b03168560e0015160405160006040518083038185875af1925050503d80600081146112fb576040519150601f19603f3d011682016040523d82523d6000602084013e611300565b606091505b50915091508161132f578460200151604001518160405163f2fc9fa160e01b815260040161033b929190611ef5565b50508260200151604001516001600160a01b03168d8d8381811061135557611355611dfa565b905060200201602081019061136a9190611cb8565b60e085015160c086015160208088015151604080519485529184019290925282015263ffffffff91909116907f25f4dfa5f0703d4c509bd7216e70f8378f419433c14840c14f3eaadb60642ad19060600160405180910390a3600101610fdf565b5050505050509695505050505050565b7f81ba2e33f1e91d5b498c91742740d1de8a6149f571c26bec9ae77b5cf099c582611404610b2c565b60408051918252602082018490520160405180910390a1610ab48161190f565b60006103e4610ad960016000805160206120fd833981519152611d97565b61144b81611467565b610ab4610aaf600160008051602061209d833981519152611d97565b6001600160a01b038116610ab45760405163f6b2911f60e01b815260040160405180910390fd5b600080611499610e36565b9050600060016114a7610e36565b546114b29190611d97565b905061150f8483836001600160401b0316815481106114d3576114d3611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505061160a565b1561151b579392505050565b600061153c8584836001600160401b0316815481106114d3576114d3611dfa565b1561154957949350505050565b8160070b8160070b1461160257600060026115648484611f5a565b61156e9190611fc1565b9050600084826001600160401b03168154811061158d5761158d611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505090506115d0878261160a565b156115df575095945050505050565b8060400151876060015110156115f7578193506115fb565b8192505b5050611549565b949350505050565b8051604082015160009161161d91611e50565b83606001511080156104d257505060400151606090910151101590565b61165e60405180606001604052806000815260200160008152602001600081525090565b6060820151805160409091015160009161167791611e50565b6020840151805160609091015191925061169a916116959084611d97565b610afb565b60208084018290526060850151805191015190916116b89190611fff565b6116c2919061201e565b8252602080840151805190820151918401516000926116e091611fff565b6116ea919061201e565b8351909150811015611724578251611703908290611d97565b6040840181905261172090611716610add565b6103759190611e50565b8083525b8260200151846020015160600181815161173e9190611e50565b905250602080840151908501518051611758908390611d97565b9052508251602080860151018051611771908390611d97565b905250602083015160c08501805161178a908390611e50565b905250825160e0850180516117a0908390611e50565b90525060408481015185516020868101518751828a0151518987015187519384529383019190915294810194909452606084015263ffffffff918216929116907f01d87b0f07b182b784c074487c5a8f901a8e2348275f5480d822a70de74fa8b79060800160405180910390a35050602082015151158015906118425750816080015163ffffffff168260400151600161183a9190612032565b63ffffffff16105b8015611856575060008260a0015161ffff16115b15611906576000611865610e36565b90508260400180516118769061205a565b63ffffffff16908163ffffffff168152505080836040015163ffffffff16815481106118a4576118a4611dfa565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505083606001819052508260a00180516118f29061207e565b61ffff1690526119018361163a565b505050565b6103d68261192b565b610ab4610aaf60016000805160206120dd833981519152611d97565b6000611935610e08565b905081602001516060015181836000015163ffffffff168154811061195c5761195c611dfa565b90600052602060002090600402016003018190555081602001516000015181836000015163ffffffff168154811061199657611996611dfa565b90600052602060002090600402016000018190555081602001516020015181836000015163ffffffff16815481106119d0576119d0611dfa565b9060005260206000209060040201600101819055505050565b60405180606001604052806000815260200160008152602001600081525090565b6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b604051806101000160405280600063ffffffff168152602001611a5c611a0a565b815260006020820152604001611a706119e9565b8152600060208201819052604082018190526060820181905260809091015290565b600060208284031215611aa457600080fd5b5035919050565b6001600160a01b0381168114610ab457600080fd5b60008060408385031215611ad357600080fd5b823591506020830135611ae581611aab565b809150509250929050565b63ffffffff91909116815260200190565b60008083601f840112611b1357600080fd5b5081356001600160401b03811115611b2a57600080fd5b6020830191508360208260051b8501011115611b4557600080fd5b9250929050565b60008060208385031215611b5f57600080fd5b82356001600160401b03811115611b7557600080fd5b611b8185828601611b01565b90969095509350505050565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160070b83529284019291840191600101611ba9565b50909695505050505050565b8015158114610ab457600080fd5b60008060008060008060808789031215611bfb57600080fd5b86356001600160401b0380821115611c1257600080fd5b611c1e8a838b01611b01565b90985096506020890135915080821115611c3757600080fd5b50611c4489828a01611b01565b9095509350506040870135611c5881611bd4565b9150606087013561ffff81168114611c6f57600080fd5b809150509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015611bc857835160ff1683529284019291840191600101611c99565b600060208284031215611cca57600080fd5b813563ffffffff81168114611cde57600080fd5b9392505050565b60008060008060408587031215611cfb57600080fd5b84356001600160401b0380821115611d1257600080fd5b611d1e88838901611b01565b90965094506020870135915080821115611d3757600080fd5b50611d4487828801611b01565b95989497509550505050565b6001600160a01b0391909116815260200190565b600060208284031215611d7657600080fd5b8135611cde81611aab565b634e487b7160e01b600052601160045260246000fd5b600082821015611da957611da9611d81565b500390565b600060208284031215611dc057600080fd5b8151611cde81611aab565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415611e2457611e24611d81565b5060010190565b600063ffffffff83811690831681811015611e4857611e48611d81565b039392505050565b60008219821115611e6357611e63611d81565b500190565b9384526020840192909252604083015263ffffffff16606082015260800190565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b600060208284031215611ed157600080fd5b8151611cde81611bd4565b600060208284031215611eee57600080fd5b5051919050565b60018060a01b038316815260006020604081840152835180604085015260005b81811015611f3157858101830151858201606001528201611f15565b81811115611f43576000606083870101525b50601f01601f191692909201606001949350505050565b6000600782810b9084900b828212801560016001603f1b0384900383131615611f8557611f85611d81565b60016001603f1b03198390038212811615611fa257611fa2611d81565b50019392505050565b634e487b7160e01b600052601260045260246000fd5b60008160070b8360070b80611fd857611fd8611fab565b60016001603f1b0319821460001982141615611ff657611ff6611d81565b90059392505050565b600081600019048311821515161561201957612019611d81565b500290565b60008261202d5761202d611fab565b500490565b600063ffffffff80831681851680830382111561205157612051611d81565b01949350505050565b600063ffffffff8083168181141561207457612074611d81565b6001019392505050565b600061ffff82168061209257612092611d81565b600019019291505056fe1ec4138404500a2a0be2c2f9b103581c2a7fa783a934f91a6cc5cc924404973c9afb091d33aa9fdd4ac2dbd1cbba78215a097062e9dbdc7b707ac5bb59049bc18ec604dcc41d380ed48f70a4a32dd9623b98c284836361a21b8992d2845ee53d82055909238c0f5e63d6f174068ebb8f51bcec9bd37de63bb68f6551feec0cfda2646970667358221220217c4fd0cfc891004dde0e311f7c7606db72a075c7156051d3cbb7d569f9775664736f6c634300080a0033

Deployed Bytecode Sourcemap

657:23402:8:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6813:353;;;;;;;;;;-1:-1:-1;6813:353:8;;;;;:::i;:::-;;:::i;:::-;;3567:101;;;;;;;;;;;;;:::i;:::-;;;345:25:116;;;333:2;318:18;3567:101:8;;;;;;;;4530:219;;;;;;;;;;-1:-1:-1;4530:219:8;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;2583:113::-;;;;;;;;;;;;;:::i;3407:117::-;;;;;;;;;;;;;:::i;3711:776::-;;;;;;;;;;-1:-1:-1;3711:776:8;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;4999:339::-;;;;;;;;;;-1:-1:-1;4999:339:8;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;2984:119::-;;;;;;;;;;;;;:::i;3146:218::-;;;;;;;;;;-1:-1:-1;3146:218:8;;;;;:::i;:::-;;:::i;:::-;;;;4845:13:116;;4827:32;;4915:4;4903:17;;;4897:24;4875:20;;;4868:54;4966:17;;;4960:24;4938:20;;;4931:54;4815:2;4800:18;3146:218:8;4613:378:116;5381:281:8;;;;;;;;;;-1:-1:-1;5381:281:8;;;;;:::i;:::-;;:::i;2739:202::-;;;;;;;;;;-1:-1:-1;2739:202:8;;;;;:::i;:::-;;:::i;:::-;;;;;;5999:13:116;;5981:32;;6069:4;6057:17;;;6051:24;6029:20;;;6022:54;6136:4;6124:17;;;6118:24;-1:-1:-1;;;;;6114:50:116;6092:20;;;6085:80;6221:4;6209:17;;;6203:24;6181:20;;;6174:54;;;;5968:3;5953:19;;5770:464;2446:94:8;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;4792:164::-;;;;;;;;;;-1:-1:-1;4792:164:8;;;;;:::i;:::-;;:::i;5705:1065::-;;;;;;:::i;:::-;;:::i;2263:140::-;;;;;;;;;;-1:-1:-1;2263:140:8;;;;;:::i;:::-;;:::i;6813:353::-;1612:18;:16;:18::i;:::-;-1:-1:-1;;;;;1598:32:8;:10;-1:-1:-1;;;;;1598:32:8;;1594:104;;1676:10;1653:34;;-1:-1:-1;;;1653:34:8;;;;;;;;:::i;:::-;;;;;;;;1594:104;6882:20:::1;6905:48;6920:26;:24;:26::i;:::-;6948:4;6905:14;:48::i;:::-;6882:71:::0;-1:-1:-1;6967:16:8;;6963:197:::1;;6999:67;7053:12;7024:26;:24;:26::i;:::-;:41;;;;:::i;:::-;6999:24;:67::i;:::-;7080:14;:12;:14::i;:::-;-1:-1:-1::0;;;;;7080:46:8::1;;7134:12;7080:69;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;6963:197;6872:294;6813:353:::0;:::o;3567:101::-;3617:7;3643:18;:16;:18::i;:::-;3636:25;;3567:101;:::o;4530:219::-;4657:22;1776:14;1793;:12;:14::i;:::-;1776:31;-1:-1:-1;1825:10:8;-1:-1:-1;;;;;1825:28:8;;;1821:156;;1886:5;-1:-1:-1;;;;;1886:18:8;;:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;1873:46:8;;1920:10;547:8:37;1873:89:8;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1821:156;1762:225;4702:40:::1;4717:12;4731:10;4702:14;:40::i;:::-;4695:47;;1996:1;4530:219:::0;;;;:::o;2583:113::-;2639:7;2665:17;:15;:17::i;:::-;:24;;2583:113;-1:-1:-1;2583:113:8:o;3407:117::-;3465:7;3491:26;:24;:26::i;3711:776::-;3826:33;3908:17;-1:-1:-1;;;;;3896:37:8;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3896:37:8;;3875:58;;3943;;:::i;:::-;4011;4072:21;:19;:21::i;:::-;4136:23;;4011:82;;-1:-1:-1;4173:26:8;;4169:123;;4237:16;4254:26;4279:1;4254:22;:26;:::i;:::-;4237:44;;;;;;;;:::i;:::-;;;;;;;;;;;4215:66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4169:123;4306:11;4301:180;4323:30;;;4301:180;;;4402:68;4426:17;;4444:3;4426:22;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;4450:19;4402:23;:68::i;:::-;4376:18;4395:3;4376:23;;;;;;;;:::i;:::-;;;;;;:94;;;;;;;;;;;4355:5;;;;:::i;:::-;;;4301:180;;;;3865:622;;;3711:776;;;;:::o;4999:339::-;5198:28;5245:86;5266:16;;5284:18;;5304;5324:6;5245:20;:86::i;:::-;5238:93;4999:339;-1:-1:-1;;;;;;;4999:339:8:o;2984:119::-;3042:7;3068:21;:19;:21::i;3146:218::-;3255:38;;:::i;:::-;3316:21;:19;:21::i;:::-;3338:18;3316:41;;;;;;;;;;:::i;:::-;;;;;;;;;;;3309:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3146:218;;;:::o;5381:281::-;5520:28;5571:84;5592:17;;5611:19;;5632:4;5638:16;5571:20;:84::i;:::-;5564:91;5381:281;-1:-1:-1;;;;;5381:281:8:o;2739:202::-;2844:32;;:::i;:::-;2899:17;:15;:17::i;:::-;2917:16;2899:35;;;;;;;;;;:::i;:::-;;;;;;;;;;2892:42;;;;;;;;2899:35;;;;;;;2892:42;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2892:42:8;;;;;;;;;;;;;;;2739:202;-1:-1:-1;;2739:202:8:o;2446:94::-;2489:7;2515:18;:16;:18::i;4792:164::-;4868:22;2058:14;2075;:12;:14::i;:::-;2058:31;;2116:5;-1:-1:-1;;;;;2116:18:8;;:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2103:46:8;;2150:10;547:8:37;2103:89:8;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2044:159;4909:40:::1;4924:12;4938:10;4909:14;:40::i;5705:1065::-:0;1612:18;:16;:18::i;:::-;-1:-1:-1;;;;;1598:32:8;:10;-1:-1:-1;;;;;1598:32:8;;1594:104;;1676:10;1653:34;;-1:-1:-1;;;1653:34:8;;;;;;;;:::i;1594:104::-;5794:20:::1;5817:18;:16;:18::i;:::-;5794:41;;5870:12;5849:18;:33;5845:134;;;5905:63;::::0;-1:-1:-1;;;5905:63:8;;::::1;::::0;::::1;8074:25:116::0;;;8115:18;;;8108:34;;;8047:18;;5905:63:8::1;7900:248:116::0;5845:134:8::1;5988:58;6049:21;:19;:21::i;:::-;6114:23:::0;;5988:82;;-1:-1:-1;6080:24:8::1;6195:9;6218:22;::::0;::::1;::::0;6214:243:::1;;6256:62;6321:16:::0;6338:21:::1;6358:1;6338:17:::0;:21:::1;:::i;:::-;6321:39;;;;;;;;;;:::i;:::-;;::::0;;;::::1;::::0;;;;6256:104:::1;::::0;;::::1;::::0;::::1;::::0;;6321:39:::1;::::0;;::::1;::::0;;::::1;6256:104:::0;;;;;::::1;::::0;::::1;::::0;;;::::1;::::0;;;;::::1;;::::0;;;;;;;;;-1:-1:-1;6383:63:8::1;::::0;6256:104;6383:63:::1;:::i;:::-;6374:72;;6242:215;6214:243;6501:101;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;;;;;;;6466:146;;::::1;::::0;;::::1;::::0;;-1:-1:-1;6466:146:8;;;;;;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;;;;::::1;::::0;;;::::1;::::0;;::::1;::::0;6622:51:::1;6639:33;6558:18:::0;6639:12;:33:::1;:::i;:::-;6622:16;:51::i;:::-;6688:75;6707:6;6715:18;6735:8;6745:17;6688:75;;;;;;;;;:::i;:::-;;;;;;;;5784:986;;;;;5705:1065:::0;:::o;2263:140::-;2328:1;1146:13:5;:11;:13::i;:::-;1134:8;:25;1130:109;;1204:8;1214:13;:11;:13::i;:::-;1182:46;;-1:-1:-1;;;1182:46:5;;;;;8074:25:116;;;;8115:18;;;8108:34;8047:18;;1182:46:5;7900:248:116;1130:109:5;1248:25;1260:12;:8;1271:1;1260:12;:::i;:::-;1248:11;:25::i;:::-;2341:24:8::1;2358:6;2341:16;:24::i;:::-;2380:16;2389:6;2380:16;;;;;;:::i;:::-;;;;;;;;1343:30:5::0;1354:8;1364;;1343:30;;;;;;;;:::i;:::-;;;;;;;;2263:140:8;;:::o;627:123:92:-;678:65;308:45;352:1;-1:-1:-1;;;;;;;;;;;308:45:92;:::i;:::-;733:9;3024:24:43;;2865:199;678:65:92;627:123;:::o;2865:199:43:-;3024:24;;2865:199::o;516:131:91:-;554:7;580:60;381:50;430:1;-1:-1:-1;;;;;;;;;;;381:50:91;:::i;:::-;881:16:43;;701:212;572:140:60;610:7;636:69;405:58;462:1;-1:-1:-1;;;;;;;;;;;405:58:60;:::i;1295:112:42:-;1355:7;1387:2;1382;:7;:17;;1397:2;1382:17;;;-1:-1:-1;1392:2:42;1295:112;-1:-1:-1;1295:112:42:o;807:136:60:-;857:79;405:58;462:1;-1:-1:-1;;;;;;;;;;;405:58:60;:::i;499:131:61:-;537:7;563:60;352:50;401:1;-1:-1:-1;;;;;;;;;;;352:50:61;:::i;11723:1285:8:-;11807:22;11841:39;11869:10;11841:27;:39::i;:::-;11894:17;11890:74;;11934:19;;-1:-1:-1;;;11934:19:8;;;;;;;;;;;11890:74;11978:14;:12;:14::i;:::-;:68;;-1:-1:-1;;;11978:68:8;;12006:10;11978:68;;;9627:34:116;12026:4:8;9677:18:116;;;9670:43;9729:18;;;9722:34;;;-1:-1:-1;;;;;11978:27:8;;;;;;;9562:18:116;;11978:68:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;11973:122;;12069:15;;-1:-1:-1;;;12069:15:8;;;;;;;;;;;11973:122;12104:50;12157:17;:15;:17::i;:::-;12209:21;;;-1:-1:-1;12104:70:8;-1:-1:-1;12241:14:8;12273:20;;;;12269:225;;12309:54;12366:14;12381:19;12399:1;12381:15;:19;:::i;:::-;12366:35;;;;;;;;;;:::i;:::-;;;;;;;;;;12309:92;;;;;;;;12366:35;;;;;;;12309:92;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;12309:92:8;;;;;;;;;;;;;;;;;;;-1:-1:-1;12424:59:8;;12309:92;12424:59;:::i;:::-;12415:68;;12295:199;12269:225;12504:24;12531:14;:12;:14::i;:::-;-1:-1:-1;;;;;12531:42:8;;12574:12;12531:56;;;;;;;;;;;;;345:25:116;;333:2;318:18;;199:177;12531:56:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;12631:198;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;12631:198:8;;;;;;;;;;;;;;;12598:241;;;;;;;;-1:-1:-1;12598:241:8;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;12598:241:8;;;;;;;;;;;;;;;12631:198;-1:-1:-1;12850:51:8;12631:198;12867:18;:16;:18::i;:::-;:33;;;;:::i;12850:51::-;12933:10;-1:-1:-1;;;;;12917:84:8;;12945:6;12953:12;12967:16;12985:15;12917:84;;;;;;;;;:::i;:::-;;;;;;;;11831:1177;;;11723:1285;;;;:::o;1039:187:62:-;1077:28;;309:49;357:1;317:36;309:49;:::i;979:193:63:-;1017:30;;329:53;381:1;337:40;329:53;:::i;9955:1497:8:-;10119:23;10154:50;10207:17;:15;:17::i;:::-;10372:21;;10154:70;;-1:-1:-1;10352:41:8;;;;10348:100;;-1:-1:-1;;10409:28:8;;;;;10348:100;10457:46;10506:14;10521:16;10506:32;;;;;;;;;;:::i;:::-;;;;;;;;;;10457:81;;;;;;;;10506:32;;;;;;;10457:81;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;10457:81:8;;;;;;;;;;;;;;;;-1:-1:-1;10655:84:8;;-1:-1:-1;;10700:28:8;;;;;;10655:84;10991:21;:19;:21::i;:::-;:28;:33;;:136;;-1:-1:-1;11107:20:8;;;;11075:27;;11045;;;;:57;;11075:27;11045:57;:::i;:::-;11044:83;;10991:136;10974:215;;;-1:-1:-1;;11152:26:8;;;;;;10974:215;11402:43;11431:13;11402:28;:43::i;20344:3444::-;20547:28;20620:17;20658:52;;;20654:116;;20733:26;;-1:-1:-1;;;20733:26:8;;;;;;;;;;;20654:116;20807:22;-1:-1:-1;;;;;20795:35:8;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;20795:35:8;;20779:51;;20841:50;20894:17;:15;:17::i;:::-;20841:70;;20921:58;20982:21;:19;:21::i;:::-;20921:82;;21014:42;;:::i;:::-;21103:23;;21066:61;;:27;;;:61;21172:21;;21137:25;21205:2577;21233:22;21227:3;:28;21205:2577;;;21344:17;;21362:3;21344:22;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;21319:47;;;;21407:19;;21427:3;21407:24;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;21380:51;;;;:24;;;:51;21543:22;;:44;;;;;;21539:138;;21639:22;;21614:48;;-1:-1:-1;;;21614:48:8;;;;21639:22;21614:48;;;:::i;21539:138::-;21797:6;:27;;;21769:55;;:6;:24;;;:55;;;21765:153;;21878:6;:24;;;21851:52;;-1:-1:-1;;;21851:52:8;;;;;;;;:::i;21765:153::-;22007:14;22022:17;;22040:3;22022:22;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;22007:38;;;;;;;;;;:::i;:::-;;;;;;;;;;21984:61;;;;;;;;22007:38;;;;;;;21984:61;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21984:61:8;;;;;;;;;;;;;;;:20;;;:61;;;22135:27;22131:361;;22191:19;22187:215;;;1555:1;22234:13;22248:3;22234:18;;;;;;;;:::i;:::-;:34;;;;:18;;;;;;;;;;;:34;22326:5;;21205:2577;;22187:215;22454:22;;22426:51;;-1:-1:-1;;;22426:51:8;;;;22454:22;22426:51;;;:::i;22131:361::-;22585:16;22602:19;;22622:3;22602:24;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;22585:42;;;;;;;;;;:::i;:::-;;;;;;;;;;22560:67;;;;;;;;;22585:42;;;;;;;22560:67;;;;;;;;;;;;;;;;;;;:22;;;:67;;;22770:20;;;;22761:54;;:8;:54::i;:::-;22756:163;;22855:22;;22879:24;;;;;22842:62;;-1:-1:-1;;;22842:62:8;;10586:10:116;10623:15;;;22842:62:8;;;10605:34:116;10675:15;;10655:18;;;10648:43;10549:18;;22842:62:8;10404:293:116;22756:163:8;22933:21;;;:12;;;:21;22987:1;22968:16;;;:20;;;23002:18;;;:22;23039:27;22933:6;23039:19;:27::i;:::-;23102:20;;;;:27;:32;:80;;1402:1;23102:80;;;1266:1;23102:80;23081:13;23095:3;23081:18;;;;;;;;:::i;:::-;;;;;;:101;;;;;;;;;;;23216:12;23230:18;23252:6;:20;;;:26;;;-1:-1:-1;;;;;23252:31:8;23291:6;:16;;;23252:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23215:97;;;;23335:7;23330:114;;23391:6;:20;;;:26;;;23419:5;23373:52;;-1:-1:-1;;;23373:52:8;;;;;;;;;:::i;23330:114::-;23197:261;;23554:6;:20;;;:26;;;-1:-1:-1;;;;;23476:233:8;23514:17;;23532:3;23514:22;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;23598:16;;;;23632:18;;;;23668:20;;;;;:27;23476:233;;;11811:25:116;;;11852:18;;;11845:34;;;;11895:18;;11888:34;23476:233:8;;;;;;;;11799:2:116;11784:18;23476:233:8;;;;;;;23752:5;;21205:2577;;;;20577:3211;;;;;20344:3444;;;;;;;;:::o;23898:159::-;23967:46;23983:18;:16;:18::i;:::-;23967:46;;;8074:25:116;;;8130:2;8115:18;;8108:34;;;8047:18;23967:46:8;;;;;;;24023:27;24040:9;24023:16;:27::i;426:125:92:-;464:7;490:54;308:45;352:1;-1:-1:-1;;;;;;;;;;;308:45:92;:::i;735:177:91:-;786:38;814:9;786:27;:38::i;:::-;834:71;381:50;430:1;-1:-1:-1;;;;;;;;;;;381:50:91;:::i;292:163:41:-;-1:-1:-1;;;;;363:22:41;;359:90;;408:30;;-1:-1:-1;;;408:30:41;;;;;;;;;;;8250:1321:8;8384:5;8405:58;8466:21;:19;:21::i;:::-;8405:82;;8498:9;8554:1;8523:21;:19;:21::i;:::-;:28;:32;;;;:::i;:::-;8498:59;;8572:55;8581:14;8597:16;8621:3;-1:-1:-1;;;;;8597:29:8;;;;;;;;;:::i;:::-;;;;;;;;;;;8572:55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:8;:55::i;:::-;8568:96;;;8650:3;8250:1321;-1:-1:-1;;;8250:1321:8:o;8568:96::-;8674:9;8702:55;8711:14;8727:16;8751:3;-1:-1:-1;;;;;8727:29:8;;;;;;;;;:::i;8702:55::-;8698:96;;;8780:3;8250:1321;-1:-1:-1;;;;8250:1321:8:o;8698:96::-;8878:3;8871:10;;:3;:10;;;8864:681;;8897:9;8923:1;8910:9;8916:3;8910;:9;:::i;:::-;8909:15;;;;:::i;:::-;8897:27;;9017:57;9077:16;9101:3;-1:-1:-1;;;;;9077:29:8;;;;;;;;;:::i;:::-;;;;;;;;;;;9017:89;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9124:44;9133:14;9149:18;9124:8;:44::i;:::-;9120:93;;;-1:-1:-1;9195:3:8;8250:1321;-1:-1:-1;;;;;8250:1321:8:o;9120:93::-;9418:18;:25;;;9394:14;:21;;;:49;9390:145;;;9469:3;9463:9;;9390:145;;;9517:3;9511:9;;9390:145;8883:662;;8864:681;;;9561:3;8250:1321;-1:-1:-1;;;;8250:1321:8:o;7651:364::-;7907:23;;7881;;;;7820:4;;7881:49;;;:::i;:::-;7857:14;:21;;;:73;:141;;;;-1:-1:-1;;7975:23:8;;;7950:21;;;;;:48;;;7651:364::o;15501:4286::-;15594:47;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;15594:47:8;15735:23;;;;:30;;15702;;;;;15665:34;;15702:63;;;:::i;:::-;16025:21;;;;:28;;16084;;;;;15665:100;;-1:-1:-1;16010:103:8;;16055:57;;15665:100;16055:57;:::i;:::-;16010:14;:103::i;:::-;15972:19;;;;:141;;;16321:23;;;;:30;;16281:36;;;16321:30;;16259:58;;16281:36;16259:58;:::i;:::-;16258:93;;;;:::i;:::-;16225:126;;16657:21;;;;;:28;;16615:38;;;;16593:19;;;;16225:14;;16593:60;;;:::i;:::-;16592:93;;;;:::i;:::-;16729:14;;16543:142;;-1:-1:-1;16704:39:8;;16700:296;;;16789:14;;:39;;16806:22;;16789:39;:::i;:::-;16763:23;;;:65;;;16846:78;;16871:26;:24;:26::i;:::-;:52;;;;:::i;16846:78::-;16942:39;;;16700:296;17807:4;:19;;;17775:7;:21;;;:28;;:51;;;;;;;:::i;:::-;;;-1:-1:-1;17872:19:8;;;;;17840:21;;;;:51;;;;17872:19;;17840:51;:::i;:::-;;;-1:-1:-1;17947:14:8;;17905:21;;;;;:38;:56;;;;17947:14;;17905:56;:::i;:::-;;;-1:-1:-1;17999:19:8;;;;17976;;;:42;;;;17999:19;;17976:42;:::i;:::-;;;-1:-1:-1;18053:14:8;;18032:17;;;:35;;;;18053:14;;18032:35;:::i;:::-;;;-1:-1:-1;18445:25:8;;;;;18404:23;;18488:19;;;;;18525:14;;18557:21;;;;:28;18603:23;;;;18364:276;;13251:25:116;;;13292:18;;;13285:34;;;;13335:18;;;13328:34;;;;13393:2;13378:18;;13371:34;18364:276:8;;;;;;;;;;13238:3:116;13223:19;18364:276:8;;;;;;;-1:-1:-1;;18977:21:8;;;;:28;:32;;;;:96;;;19045:7;:28;;;19013:60;;:7;:25;;;19041:1;19013:29;;;;:::i;:::-;:60;;;18977:96;:133;;;;;19109:1;19093:7;:13;;;:17;;;18977:133;18960:821;;;19135:58;19196:21;:19;:21::i;:::-;19135:82;;19234:7;:25;;19232:27;;;;;:::i;:::-;;;;;;;;;;;19299:16;19316:7;:25;;;19299:43;;;;;;;;;;:::i;:::-;;;;;;;;;;;19273:69;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:7;:23;;:69;;;;19358:7;:13;;19356:15;;;;;:::i;:::-;;;;;19386:28;19406:7;19386:19;:28::i;:::-;19121:304;6872:294:::1;6813:353:::0;:::o;18960:821::-;19743:27;19762:7;19743:18;:27::i;719:127:61:-;769:70;352:50;401:1;-1:-1:-1;;;;;;;;;;;352:50:61;:::i;14798:450:8:-;14890:50;14943:17;:15;:17::i;:::-;14890:70;;15019:7;:21;;;:28;;;14970:14;14985:7;:23;;;14970:39;;;;;;;;;;:::i;:::-;;;;;;;;;;;:46;;:77;;;;15106:7;:21;;;:28;;;15057:14;15072:7;:23;;;15057:39;;;;;;;;;;:::i;:::-;;;;;;;;;;;:46;;:77;;;;15203:7;:21;;;:38;;;15144:14;15159:7;:23;;;15144:39;;;;;;;;;;:::i;:::-;;;;;;;;;;;:56;;:97;;;;14880:368;14798:450;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:180:116:-;73:6;126:2;114:9;105:7;101:23;97:32;94:52;;;142:1;139;132:12;94:52;-1:-1:-1;165:23:116;;14:180;-1:-1:-1;14:180:116:o;381:131::-;-1:-1:-1;;;;;456:31:116;;446:42;;436:70;;502:1;499;492:12;517:315;585:6;593;646:2;634:9;625:7;621:23;617:32;614:52;;;662:1;659;652:12;614:52;698:9;685:23;675:33;;758:2;747:9;743:18;730:32;771:31;796:5;771:31;:::i;:::-;821:5;811:15;;;517:315;;;;;:::o;837:192::-;1011:10;999:23;;;;981:42;;969:2;954:18;;837:192::o;1034:366::-;1096:8;1106:6;1160:3;1153:4;1145:6;1141:17;1137:27;1127:55;;1178:1;1175;1168:12;1127:55;-1:-1:-1;1201:20:116;;-1:-1:-1;;;;;1233:30:116;;1230:50;;;1276:1;1273;1266:12;1230:50;1313:4;1305:6;1301:17;1289:29;;1373:3;1366:4;1356:6;1353:1;1349:14;1341:6;1337:27;1333:38;1330:47;1327:67;;;1390:1;1387;1380:12;1327:67;1034:366;;;;;:::o;1405:435::-;1490:6;1498;1551:2;1539:9;1530:7;1526:23;1522:32;1519:52;;;1567:1;1564;1557:12;1519:52;1594:23;;-1:-1:-1;;;;;1629:30:116;;1626:50;;;1672:1;1669;1662:12;1626:50;1711:69;1772:7;1763:6;1752:9;1748:22;1711:69;:::i;:::-;1799:8;;1685:95;;-1:-1:-1;1405:435:116;-1:-1:-1;;;;1405:435:116:o;1845:643::-;2012:2;2064:21;;;2134:13;;2037:18;;;2156:22;;;1983:4;;2012:2;2235:15;;;;2209:2;2194:18;;;1983:4;2278:184;2292:6;2289:1;2286:13;2278:184;;;2367:13;;2364:1;2353:28;2341:41;;2437:15;;;;2402:12;;;;2314:1;2307:9;2278:184;;;-1:-1:-1;2479:3:116;;1845:643;-1:-1:-1;;;;;;1845:643:116:o;2493:118::-;2579:5;2572:13;2565:21;2558:5;2555:32;2545:60;;2601:1;2598;2591:12;2616:1067;2750:6;2758;2766;2774;2782;2790;2843:3;2831:9;2822:7;2818:23;2814:33;2811:53;;;2860:1;2857;2850:12;2811:53;2887:23;;-1:-1:-1;;;;;2959:14:116;;;2956:34;;;2986:1;2983;2976:12;2956:34;3025:69;3086:7;3077:6;3066:9;3062:22;3025:69;:::i;:::-;3113:8;;-1:-1:-1;2999:95:116;-1:-1:-1;3201:2:116;3186:18;;3173:32;;-1:-1:-1;3217:16:116;;;3214:36;;;3246:1;3243;3236:12;3214:36;;3285:71;3348:7;3337:8;3326:9;3322:24;3285:71;:::i;:::-;3375:8;;-1:-1:-1;3259:97:116;-1:-1:-1;;3460:2:116;3445:18;;3432:32;3473:28;3432:32;3473:28;:::i;:::-;3520:5;-1:-1:-1;3577:2:116;3562:18;;3549:32;3625:6;3612:20;;3600:33;;3590:61;;3647:1;3644;3637:12;3590:61;3670:7;3660:17;;;2616:1067;;;;;;;;:::o;3688:639::-;3855:2;3907:21;;;3977:13;;3880:18;;;3999:22;;;3826:4;;3855:2;4078:15;;;;4052:2;4037:18;;;3826:4;4121:180;4135:6;4132:1;4129:13;4121:180;;;4200:13;;4215:4;4196:24;4184:37;;4276:15;;;;4241:12;;;;4157:1;4150:9;4121:180;;4332:276;4390:6;4443:2;4431:9;4422:7;4418:23;4414:32;4411:52;;;4459:1;4456;4449:12;4411:52;4498:9;4485:23;4548:10;4541:5;4537:22;4530:5;4527:33;4517:61;;4574:1;4571;4564:12;4517:61;4597:5;4332:276;-1:-1:-1;;;4332:276:116:o;4996:769::-;5116:6;5124;5132;5140;5193:2;5181:9;5172:7;5168:23;5164:32;5161:52;;;5209:1;5206;5199:12;5161:52;5236:23;;-1:-1:-1;;;;;5308:14:116;;;5305:34;;;5335:1;5332;5325:12;5305:34;5374:69;5435:7;5426:6;5415:9;5411:22;5374:69;:::i;:::-;5462:8;;-1:-1:-1;5348:95:116;-1:-1:-1;5550:2:116;5535:18;;5522:32;;-1:-1:-1;5566:16:116;;;5563:36;;;5595:1;5592;5585:12;5563:36;;5634:71;5697:7;5686:8;5675:9;5671:24;5634:71;:::i;:::-;4996:769;;;;-1:-1:-1;5724:8:116;-1:-1:-1;;;;4996:769:116:o;6239:203::-;-1:-1:-1;;;;;6403:32:116;;;;6385:51;;6373:2;6358:18;;6239:203::o;6447:247::-;6506:6;6559:2;6547:9;6538:7;6534:23;6530:32;6527:52;;;6575:1;6572;6565:12;6527:52;6614:9;6601:23;6633:31;6658:5;6633:31;:::i;6699:127::-;6760:10;6755:3;6751:20;6748:1;6741:31;6791:4;6788:1;6781:15;6815:4;6812:1;6805:15;6831:125;6871:4;6899:1;6896;6893:8;6890:34;;;6904:18;;:::i;:::-;-1:-1:-1;6941:9:116;;6831:125::o;6961:251::-;7031:6;7084:2;7072:9;7063:7;7059:23;7055:32;7052:52;;;7100:1;7097;7090:12;7052:52;7132:9;7126:16;7151:31;7176:5;7151:31;:::i;7217:274::-;-1:-1:-1;;;;;7409:32:116;;;;7391:51;;7473:2;7458:18;;7451:34;7379:2;7364:18;;7217:274::o;7496:127::-;7557:10;7552:3;7548:20;7545:1;7538:31;7588:4;7585:1;7578:15;7612:4;7609:1;7602:15;7628:127;7689:10;7684:3;7680:20;7677:1;7670:31;7720:4;7717:1;7710:15;7744:4;7741:1;7734:15;7760:135;7799:3;-1:-1:-1;;7820:17:116;;7817:43;;;7840:18;;:::i;:::-;-1:-1:-1;7887:1:116;7876:13;;7760:135::o;8153:221::-;8192:4;8221:10;8281;;;;8251;;8303:12;;;8300:38;;;8318:18;;:::i;:::-;8355:13;;8153:221;-1:-1:-1;;;8153:221:116:o;8379:128::-;8419:3;8450:1;8446:6;8443:1;8440:13;8437:39;;;8456:18;;:::i;:::-;-1:-1:-1;8492:9:116;;8379:128::o;8512:406::-;8741:25;;;8797:2;8782:18;;8775:34;;;;8840:2;8825:18;;8818:34;8900:10;8888:23;8883:2;8868:18;;8861:51;8728:3;8713:19;;8512:406::o;8923:459::-;9108:6;9097:9;9090:25;9151:2;9146;9135:9;9131:18;9124:30;9190:6;9185:2;9174:9;9170:18;9163:34;9247:6;9239;9234:2;9223:9;9219:18;9206:48;9303:1;9274:22;;;9298:2;9270:31;;;9263:42;;;;9366:2;9345:15;;;-1:-1:-1;;9341:29:116;9326:45;9322:54;;8923:459;-1:-1:-1;;8923:459:116:o;9767:245::-;9834:6;9887:2;9875:9;9866:7;9862:23;9858:32;9855:52;;;9903:1;9900;9893:12;9855:52;9935:9;9929:16;9954:28;9976:5;9954:28;:::i;10017:184::-;10087:6;10140:2;10128:9;10119:7;10115:23;10111:32;10108:52;;;10156:1;10153;10146:12;10108:52;-1:-1:-1;10179:16:116;;10017:184;-1:-1:-1;10017:184:116:o;10912:692::-;11116:1;11112;11107:3;11103:11;11099:19;11091:6;11087:32;11076:9;11069:51;11050:4;11139:2;11177;11172;11161:9;11157:18;11150:30;11209:6;11203:13;11252:6;11247:2;11236:9;11232:18;11225:34;11277:1;11287:140;11301:6;11298:1;11295:13;11287:140;;;11396:14;;;11392:23;;11386:30;11362:17;;;11381:2;11358:26;11351:66;11316:10;;11287:140;;;11445:6;11442:1;11439:13;11436:91;;;11515:1;11510:2;11501:6;11490:9;11486:22;11482:31;11475:42;11436:91;-1:-1:-1;11588:2:116;11567:15;-1:-1:-1;;11563:29:116;11548:45;;;;11595:2;11544:54;;10912:692;-1:-1:-1;;;;10912:692:116:o;11933:361::-;11971:3;12011:1;12000:16;;;;12036;;;12071:11;;;12098:10;;-1:-1:-1;;;;;12119:28:116;;;12110:38;;12094:55;12091:81;;;12152:18;;:::i;:::-;-1:-1:-1;;;;;;12201:33:116;;;12192:43;;12184:52;;12181:78;;;12239:18;;:::i;:::-;-1:-1:-1;12275:13:116;;11933:361;-1:-1:-1;;;11933:361:116:o;12299:127::-;12360:10;12355:3;12351:20;12348:1;12341:31;12391:4;12388:1;12381:15;12415:4;12412:1;12405:15;12431:286;12469:1;12510;12507;12496:16;12546:1;12543;12532:16;12567:3;12557:37;;12574:18;;:::i;:::-;-1:-1:-1;;;;;;12610:32:116;;-1:-1:-1;;12644:15:116;;12606:54;12603:80;;;12663:18;;:::i;:::-;12697:14;;;12431:286;-1:-1:-1;;;12431:286:116:o;12722:168::-;12762:7;12828:1;12824;12820:6;12816:14;12813:1;12810:21;12805:1;12798:9;12791:17;12787:45;12784:71;;;12835:18;;:::i;:::-;-1:-1:-1;12875:9:116;;12722:168::o;12895:120::-;12935:1;12961;12951:35;;12966:18;;:::i;:::-;-1:-1:-1;13000:9:116;;12895:120::o;13416:228::-;13455:3;13483:10;13520:2;13517:1;13513:10;13550:2;13547:1;13543:10;13581:3;13577:2;13573:12;13568:3;13565:21;13562:47;;;13589:18;;:::i;:::-;13625:13;;13416:228;-1:-1:-1;;;;13416:228:116:o;13649:201::-;13687:3;13715:10;13760:2;13753:5;13749:14;13787:2;13778:7;13775:15;13772:41;;;13793:18;;:::i;:::-;13842:1;13829:15;;13649:201;-1:-1:-1;;;13649:201:116:o;13855:181::-;13893:3;13937:6;13930:5;13926:18;13963:7;13953:41;;13974:18;;:::i;:::-;-1:-1:-1;;14010:20:116;;13855:181;-1:-1:-1;;13855:181:116:o

Swarm Source

ipfs://217c4fd0cfc891004dde0e311f7c7606db72a075c7156051d3cbb7d569f97756

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

Liquid Collective is a secure liquid staking protocol designed to meet the needs of institutions, built and run by a collective of industry-leading Web3 teams and governed in a decentralized manner by a broad and dispersed community.

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.