ETH Price: $2,407.19 (-8.98%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

ContractCreator

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AlephAVS

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAlephAVS} from "./IAlephAVS.sol";
import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20 as IERC20Eigen} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import {IAlephVaultDeposit} from "Aleph/src/interfaces/IAlephVaultDeposit.sol";
import {IAlephVaultFactory} from "Aleph/src/interfaces/IAlephVaultFactory.sol";
import {IAlephVault} from "Aleph/src/interfaces/IAlephVault.sol";
import {IAlephVaultRedeem} from "Aleph/src/interfaces/IAlephVaultRedeem.sol";
import {IAccountant} from "Aleph/src/interfaces/IAccountant.sol";
import {IERC20Factory} from "./interfaces/IERC20Factory.sol";
import {ERC20Factory} from "./ERC20Factory.sol";
import {AlephAVSPausable} from "./AlephAVSPausable.sol";
import {IMintableBurnableERC20} from "./interfaces/IMintableBurnableERC20.sol";
import {AlephSlashing} from "./libraries/AlephSlashing.sol";
import {AlephVaultManagement} from "./libraries/AlephVaultManagement.sol";
import {RewardsManagement} from "./libraries/RewardsManagement.sol";
import {UnallocateManagement} from "./libraries/UnallocateManagement.sol";
import {AlephValidation} from "./libraries/AlephValidation.sol";

contract AlephAVS is IAlephAVS, AlephAVSPausable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant OWNER = keccak256("OWNER");

    uint32 private constant LST_STRATEGIES_OPERATOR_SET_ID = 0;

    IAllocationManager public immutable ALLOCATION_MANAGER;
    IDelegationManager public immutable DELEGATION_MANAGER;
    IStrategyManager public immutable STRATEGY_MANAGER;
    IRewardsCoordinator public immutable REWARDS_COORDINATOR;
    IAlephVaultFactory public immutable VAULT_FACTORY;
    IStrategyFactory public immutable STRATEGY_FACTORY;

    struct AVSStorage {
        IERC20Factory erc20Factory;
        mapping(address vault => IStrategy slashedStrategy) vaultToSlashedStrategy;
        mapping(address vault => IStrategy originalStrategy) vaultToOriginalStrategy;
        mapping(address vault => uint8 classId) vaultToClassId;
        mapping(uint32 operatorSetId => mapping(address strategy => bool exists)) strategyExists;
        mapping(address user => mapping(address vault => uint256 estAmountToRedeem)) pendingUnallocate;
        mapping(address vault => uint256 totalEstAmount) totalPendingUnallocate;
        mapping(address vault => uint256 withdrawnAmount) vaultWithdrawnAmount;
    }

    bytes32 private constant AVS_STORAGE_LOCATION = 0x032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db604100;

    function _getAVSStorage() private pure returns (AVSStorage storage $) {
        assembly {
            $.slot := AVS_STORAGE_LOCATION
        }
    }

    string private constant SLASH_DESCRIPTION = "";
    string private constant REWARDS_DESCRIPTION = "";

    function _validVault(address _vault) private view {
        if (_getAVSStorage().vaultToSlashedStrategy[_vault] == IStrategy(address(0))) {
            revert VaultNotInitialized(_vault);
        }
    }

    modifier validVault(address _vault) {
        _validVault(_vault);
        _;
    }

    function _onlyOperator() private view {
        if (!DELEGATION_MANAGER.isOperator(msg.sender)) revert NotRegisteredOperator();
    }

    modifier onlyOperator() {
        _onlyOperator();
        _;
    }

    function _validAmount(uint256 _amount) private pure {
        if (_amount == 0) revert InvalidAmount();
    }

    modifier validAmount(uint256 _amount) {
        _validAmount(_amount);
        _;
    }

    constructor(
        IAllocationManager _allocationManager,
        IDelegationManager _delegationManager,
        IStrategyManager _strategyManager,
        IRewardsCoordinator _rewardsCoordinator,
        IAlephVaultFactory _vaultFactory,
        IStrategyFactory _strategyFactory
    ) {
        if (address(_allocationManager) == address(0)) {
            revert InvalidAllocationManager();
        }
        if (address(_delegationManager) == address(0)) revert InvalidDelegationManager();
        if (address(_strategyManager) == address(0)) revert InvalidStrategyManager();
        if (address(_rewardsCoordinator) == address(0)) revert InvalidRewardsCoordinator();
        if (address(_vaultFactory) == address(0)) revert InvalidVaultFactory();
        if (address(_strategyFactory) == address(0)) revert InvalidStrategyFactory();

        ALLOCATION_MANAGER = _allocationManager;
        DELEGATION_MANAGER = _delegationManager;
        STRATEGY_MANAGER = _strategyManager;
        REWARDS_COORDINATOR = _rewardsCoordinator;
        VAULT_FACTORY = _vaultFactory;
        STRATEGY_FACTORY = _strategyFactory;

        _disableInitializers();
    }

    function initialize(address _owner, address _guardian, string memory _metadataURI) external initializer {
        _initialize(_owner, _guardian, _metadataURI);
    }

    function _initialize(address _owner, address _guardian, string memory _metadataURI) internal onlyInitializing {
        if (_owner == address(0) || _guardian == address(0)) revert InvalidAmount();

        __AccessControl_init();
        _pausableInit(_owner, _guardian);
        _grantRole(DEFAULT_ADMIN_ROLE, _owner);
        _grantRole(OWNER, _owner);

        ALLOCATION_MANAGER.updateAVSMetadataURI(address(this), _metadataURI);

        _initializeOperatorSet();

        ERC20Factory _erc20Factory = new ERC20Factory(address(this));
        _getAVSStorage().erc20Factory = IERC20Factory(address(_erc20Factory));
    }

    function erc20Factory() external view returns (IERC20Factory) {
        return _getAVSStorage().erc20Factory;
    }

    function vaultToSlashedStrategy(address _vault) external view returns (IStrategy _slashedStrategy) {
        _slashedStrategy = _getAVSStorage().vaultToSlashedStrategy[_vault];
    }

    function vaultToOriginalStrategy(address _vault) external view returns (IStrategy _originalStrategy) {
        _originalStrategy = _getAVSStorage().vaultToOriginalStrategy[_vault];
    }

    /**
     * @notice Returns the pending unallocation status for a user and vault
     * @dev This view function allows users to check if they can call completeUnallocate
     * @param _user The user address to check
     * @param _alephVault The Aleph vault address
     * @return userPendingAmount The user's pending unallocation amount
     * @return totalPendingAmount The total pending unallocation amount for the vault
     * @return redeemableAmount The amount currently redeemable from the vault
     * @return canComplete Whether the user can complete unallocation (has pending amount and vault has redeemable amount)
     */
    function getPendingUnallocateStatus(address _user, address _alephVault)
        external
        view
        returns (uint256 userPendingAmount, uint256 totalPendingAmount, uint256 redeemableAmount, bool canComplete)
    {
        AVSStorage storage $ = _getAVSStorage();
        userPendingAmount = $.pendingUnallocate[_user][_alephVault];
        totalPendingAmount = $.totalPendingUnallocate[_alephVault];

        // Get redeemable amount from vault
        redeemableAmount = IAlephVault(_alephVault).redeemableAmount(address(this));

        // User can complete if they have pending amount and funds are available
        canComplete = userPendingAmount > 0 && (redeemableAmount > 0 || $.vaultWithdrawnAmount[_alephVault] > 0);
    }

    /**
     * @notice Calculates the expected amount that will be withdrawn in completeUnallocate
     * @dev View function to get expected amount for signature generation. See interface documentation for details.
     *
     * @param _user The user address to calculate for
     * @param _alephVault The Aleph vault address
     * @return expectedAmount The expected amount that will be withdrawn and deposited to strategy
     */
    function calculateCompleteUnallocateAmount(address _user, address _alephVault)
        external
        view
        returns (uint256 expectedAmount)
    {
        AVSStorage storage $ = _getAVSStorage();
        uint256 _userPendingAmount = $.pendingUnallocate[_user][_alephVault];
        uint256 _totalPending = $.totalPendingUnallocate[_alephVault];
        uint256 _vaultRedeemableAmount = IAlephVault(_alephVault).redeemableAmount(address(this));
        uint256 _withdrawnAmount = $.vaultWithdrawnAmount[_alephVault];

        expectedAmount = UnallocateManagement.calculateCompleteUnallocateAmountView(
            _userPendingAmount, _totalPending, _vaultRedeemableAmount, _withdrawnAmount
        );
    }

    function registerOperator(
        address _operator,
        address _avs,
        uint32[] calldata _operatorSetIds,
        bytes calldata /* data */
    )
        external
    {
        _validateAVSRegistrarCall(_avs);
        emit OperatorRegistered(_operator, _operatorSetIds);
    }

    function deregisterOperator(address _operator, address _avs, uint32[] calldata _operatorSetIds) external {
        _validateAVSRegistrarCall(_avs);
        emit OperatorDeregistered(_operator, _operatorSetIds);
    }

    function supportsAVS(address _avs) external view returns (bool) {
        return _avs == address(this);
    }

    /// @notice Corrects vaultWithdrawnAmount to fix shortage
    function correctWithdrawnAmount(address _v, uint256 _a) external onlyRole(OWNER) {
        _getAVSStorage().vaultWithdrawnAmount[_v] += _a;
    }

    function initializeVault(uint8 _classId, address _vault, IStrategy _lstStrategy)
        external
        onlyRole(OWNER)
        returns (IStrategy _slashedStrategy)
    {
        AVSStorage storage $ = _getAVSStorage();

        if (!VAULT_FACTORY.isValidVault(_vault)) revert InvalidVault();
        if ($.vaultToSlashedStrategy[_vault] != IStrategy(address(0))) revert VaultAlreadyInitialized();

        // Validate the provided strategy
        AlephValidation.validateStrategy(address(_lstStrategy));

        // Get the original token from the LST strategy's underlying token
        // This is the token that will be used to create the slashed token
        // Note: underlyingToken() returns IERC20Eigen, so we use that type
        IERC20Eigen _originalTokenEigen = _lstStrategy.underlyingToken();
        IERC20 _originalToken = IERC20(address(_originalTokenEigen));

        // Validate that the vault's underlying token matches the LST strategy's underlying token
        if (address(IAlephVault(_vault).underlyingToken()) != address(_originalToken)) {
            revert InvalidStrategy();
        }

        IERC20 _slashedToken;
        (_slashedToken, _slashedStrategy) = AlephVaultManagement.createSlashedStrategyForVault(
            STRATEGY_FACTORY, _vault, _originalToken, $.vaultToSlashedStrategy, $.erc20Factory
        );
        $.vaultToClassId[_vault] = _classId;
        $.vaultToOriginalStrategy[_vault] = _lstStrategy;

        _addStrategyIfNotExists(LST_STRATEGIES_OPERATOR_SET_ID, _lstStrategy);

        emit VaultInitialized(
            _vault, _classId, address(_originalToken), address(_slashedToken), address(_slashedStrategy)
        );
    }

    function allocate(address _alephVault, IAlephVaultDeposit.RequestDepositParams calldata _requestDepositParams)
        external
        nonReentrant
        onlyOperator
        whenFlowNotPaused(ALLOCATE_FLOW)
        validVault(_alephVault)
        validAmount(_requestDepositParams.amount)
    {
        RewardsManagement.validateOperatorSplit(REWARDS_COORDINATOR, msg.sender, address(this));

        AVSStorage storage $ = _getAVSStorage();

        if ($.vaultToClassId[_alephVault] != _requestDepositParams.classId) revert InvalidClassId();

        (, IStrategy _stakerStrategy) = _getVaultTokenAndStrategy(_alephVault);
        IStrategy _stakerSlashedStrategy = _getSlashedStrategy(_alephVault);

        uint256 _tokenAmount = _slashAndTransferInternal(
            msg.sender, _requestDepositParams.amount, _stakerStrategy, LST_STRATEGIES_OPERATOR_SET_ID, address(this)
        );

        IERC20Eigen _slashedToken = IERC20Eigen(_stakerSlashedStrategy.underlyingToken());
        uint256 _amountToMint = AlephVaultManagement.calculateAmountToMint(
            _requestDepositParams.classId, _alephVault, address(_slashedToken), _tokenAmount
        );

        uint256 _vaultShares =
            AlephVaultManagement.depositToAlephVault(_alephVault, _requestDepositParams, _tokenAmount);
        IMintableBurnableERC20(address(_slashedToken)).mint(address(this), _amountToMint);

        // Update operator allocations in Accountant for fee distribution
        address _accountant = IAlephVault(_alephVault).accountant();
        if (_accountant != address(0)) {
            IAccountant(_accountant).setOperatorAllocations(_alephVault, msg.sender, _tokenAmount);
        }

        RewardsManagement.submitOperatorDirectedRewards(
            REWARDS_COORDINATOR,
            address(this),
            msg.sender,
            _stakerStrategy,
            _slashedToken,
            _amountToMint,
            REWARDS_DESCRIPTION
        );

        emit AllocatedToAlephVault(
            msg.sender,
            _alephVault,
            address(_stakerStrategy),
            address(_stakerSlashedStrategy),
            _tokenAmount,
            _amountToMint,
            _vaultShares,
            _requestDepositParams.classId
        );
    }

    /**
     * @notice Requests to unallocate funds by redeeming slashed tokens from vault
     * @dev First step of two-step unallocate flow. See interface documentation for detailed usage.
     *
     * @param _alephVault The Aleph vault address to unallocate from
     * @param _tokenAmount The amount of slashed strategy tokens to unallocate
     * @return batchId The batch ID for the redeem request
     * @return estAmountToRedeem The estimated amount that will be redeemed from the vault
     */
    function requestUnallocate(address _alephVault, uint256 _tokenAmount)
        external
        nonReentrant
        whenFlowNotPaused(UNALLOCATE_FLOW)
        validAmount(_tokenAmount)
        validVault(_alephVault)
        returns (uint48 batchId, uint256 estAmountToRedeem)
    {
        AVSStorage storage $ = _getAVSStorage();
        uint8 _classId = $.vaultToClassId[_alephVault];
        IStrategy _slashedStrategy = _getSlashedStrategy(_alephVault);

        estAmountToRedeem = AlephVaultManagement.calculateAmountToRedeem(
            _classId, _alephVault, address(_slashedStrategy.underlyingToken()), _tokenAmount
        );

        if (estAmountToRedeem == 0) revert InvalidAmount();

        AlephVaultManagement.validateAndBurnSlashedTokens(msg.sender, _slashedStrategy, _tokenAmount, address(this));

        IAlephVaultRedeem.RedeemRequestParams memory _p =
            IAlephVaultRedeem.RedeemRequestParams({classId: _classId, estAmountToRedeem: estAmountToRedeem});
        IAlephVaultRedeem(_alephVault).syncRedeem(_p);
        $.vaultWithdrawnAmount[_alephVault] += estAmountToRedeem;
        batchId = 0;

        $.pendingUnallocate[msg.sender][_alephVault] += estAmountToRedeem;
        $.totalPendingUnallocate[_alephVault] += estAmountToRedeem;

        emit UnallocateRequested(
            msg.sender, _alephVault, address(_slashedStrategy), _tokenAmount, estAmountToRedeem, batchId, _classId
        );

        return (batchId, estAmountToRedeem);
    }

    /**
     * @notice Completes the unallocation by withdrawing redeemable amount and depositing back to strategy
     * @dev Second step of two-step unallocate flow. See interface documentation for detailed usage.
     *
     * @param _alephVault The Aleph vault address to complete unallocation from
     * @param _strategyDepositExpiry The expiry timestamp for the strategy deposit signature
     * @param _strategyDepositSignature The caller's signature for depositing back into the original LST strategy
     * @return _amount The amount of tokens redeemed from the vault and deposited to the strategy
     * @return _shares The shares received in the original LST strategy
     */
    function completeUnallocate(
        address _alephVault,
        uint256 _strategyDepositExpiry,
        bytes calldata _strategyDepositSignature
    )
        external
        nonReentrant
        whenFlowNotPaused(UNALLOCATE_FLOW)
        validVault(_alephVault)
        returns (uint256 _amount, uint256 _shares)
    {
        AVSStorage storage $ = _getAVSStorage();
        uint8 _classId = $.vaultToClassId[_alephVault];
        IStrategy _slashedStrategy = _getSlashedStrategy(_alephVault);
        (IERC20 _vaultToken, IStrategy _originalStrategy) = _getVaultTokenAndStrategy(_alephVault);

        uint256 _userPendingAmount = $.pendingUnallocate[msg.sender][_alephVault];
        if (_userPendingAmount == 0) revert NoPendingUnallocation();

        // Calculate expected amount first (before withdrawing) so signature can be generated
        // Use the stored estAmountToRedeem as the expected amount
        uint256 _totalPending = $.totalPendingUnallocate[_alephVault];
        if (_totalPending == 0 || _totalPending < _userPendingAmount) revert InvalidAmount();

        uint256 _vaultRedeemableAmount = IAlephVault(_alephVault).redeemableAmount(address(this));
        uint256 _withdrawnAmount = $.vaultWithdrawnAmount[_alephVault];

        // Calculate expected amount using view function (before state changes)
        uint256 _expectedAmount = UnallocateManagement.calculateCompleteUnallocateAmountView(
            _userPendingAmount, _totalPending, _vaultRedeemableAmount, _withdrawnAmount
        );

        if (_expectedAmount == 0) revert InvalidAmount();

        // Step 1: Withdraw redeemable amount from vault and calculate total available
        uint256 _totalRedeemableAmount = UnallocateManagement.withdrawAndCalculateAvailable(
            _alephVault, _vaultToken, _vaultRedeemableAmount, _withdrawnAmount
        );

        // Step 2: Validate expected amount but don't cap it
        _amount = _expectedAmount;
        if (_amount == 0) revert InvalidAmount();
        if (_amount > _totalRedeemableAmount) revert InvalidAmount();
        uint256 _contractBalance = _vaultToken.balanceOf(address(this));
        if (_amount > _contractBalance) revert InvalidAmount();

        // Step 3: Update storage
        // Calculate new values using _totalRedeemableAmount directly (total available after withdrawal)
        (uint256 _newVaultWithdrawnAmount, uint256 _newTotalPending) = UnallocateManagement.calculateUnallocationStorageUpdates(
            _totalRedeemableAmount, _amount, _userPendingAmount, _totalPending
        );

        $.vaultWithdrawnAmount[_alephVault] = _newVaultWithdrawnAmount;
        $.pendingUnallocate[msg.sender][_alephVault] = 0;
        $.totalPendingUnallocate[_alephVault] = _newTotalPending;

        // Step 4: Deposit to strategy (interaction)
        _shares = AlephVaultManagement.depositToOriginalStrategy(
            STRATEGY_MANAGER,
            _originalStrategy,
            _vaultToken,
            msg.sender,
            _amount,
            _strategyDepositExpiry,
            _strategyDepositSignature
        );
        emit UnallocateCompleted(
            msg.sender, _alephVault, address(_originalStrategy), address(_slashedStrategy), _amount, _shares, _classId
        );
    }

    function calculateUnallocateAmount(address _alephVault, uint256 _tokenAmount)
        external
        view
        validVault(_alephVault)
        returns (uint256 _estimatedAmount, IStrategy _strategy, IERC20 _token)
    {
        IStrategy _slashedStrategy = _getSlashedStrategy(_alephVault);
        _estimatedAmount = AlephVaultManagement.calculateAmountToRedeem(
            _getAVSStorage().vaultToClassId[_alephVault],
            _alephVault,
            address(_slashedStrategy.underlyingToken()),
            _tokenAmount
        );
        (_token, _strategy) = _getVaultTokenAndStrategy(_alephVault);
    }

    function _addStrategyIfNotExists(uint32 _operatorSetId, IStrategy _strategy) private {
        AVSStorage storage $ = _getAVSStorage();

        if ($.strategyExists[_operatorSetId][address(_strategy)]) return;

        IStrategy[] memory arr = new IStrategy[](1);
        arr[0] = _strategy;
        ALLOCATION_MANAGER.addStrategiesToOperatorSet(address(this), _operatorSetId, arr);

        $.strategyExists[_operatorSetId][address(_strategy)] = true;
    }

    function _slashAndTransferInternal(
        address _operator,
        uint256 _amount,
        IStrategy _strategy,
        uint32 _operatorSetId,
        address _recipient
    ) private returns (uint256 _tokenAmount) {
        OperatorSet memory _operatorSet = OperatorSet(address(this), _operatorSetId);
        if (!ALLOCATION_MANAGER.isOperatorSet(_operatorSet)) revert InvalidOperatorSet();
        if (!ALLOCATION_MANAGER.isMemberOfOperatorSet(_operator, _operatorSet)) revert NotMemberOfOperatorSet();

        uint64 _magnitudeToSlash =
            AlephSlashing.calculateMagnitudeFromAmount(ALLOCATION_MANAGER, _operator, _operatorSet, _amount, _strategy);
        AlephSlashing.verifyOperatorAllocation(
            ALLOCATION_MANAGER, _operator, _operatorSet, _magnitudeToSlash, _strategy
        );

        uint256 _slashId = AlephSlashing.executeSlashAndGetId(
            ALLOCATION_MANAGER,
            address(this),
            _operator,
            _operatorSetId,
            _magnitudeToSlash,
            _strategy,
            SLASH_DESCRIPTION
        );

        _tokenAmount = AlephSlashing.clearRedistributableShares(
            STRATEGY_MANAGER, address(this), _operatorSetId, _slashId, _strategy
        );
        if (_tokenAmount == 0) revert NoTokensReceived();

        IERC20 _underlyingToken = IERC20(address(_strategy.underlyingToken()));
        _underlyingToken.safeTransfer(_recipient, _tokenAmount);

        emit SlashExecuted(
            _operator, _operatorSetId, address(_strategy), address(_underlyingToken), _tokenAmount, _slashId
        );
    }

    function _getSlashedStrategy(address _vault) internal view returns (IStrategy _slashedStrategy) {
        _slashedStrategy = _getAVSStorage().vaultToSlashedStrategy[_vault];
        if (address(_slashedStrategy) == address(0)) revert InvalidStrategy();
    }

    function _getVaultTokenAndStrategy(address _vault) internal view returns (IERC20 _vaultToken, IStrategy _strategy) {
        AVSStorage storage $ = _getAVSStorage();
        _strategy = $.vaultToOriginalStrategy[_vault];
        if (address(_strategy) == address(0)) revert InvalidStrategy();
        _vaultToken = IERC20(IAlephVault(_vault).underlyingToken());
    }

    function _validateAVSRegistrarCall(address _avs) private view {
        if (msg.sender != address(ALLOCATION_MANAGER) || _avs != address(this)) {
            revert Unauthorized();
        }
    }

    function _initializeOperatorSet() private {
        IStrategy[] memory _lstStrategies = new IStrategy[](0);

        IAllocationManagerTypes.CreateSetParams memory lstCreateParams = IAllocationManagerTypes.CreateSetParams({
            operatorSetId: LST_STRATEGIES_OPERATOR_SET_ID, strategies: _lstStrategies
        });

        IAllocationManagerTypes.CreateSetParams[] memory _params = new IAllocationManagerTypes.CreateSetParams[](1);
        _params[0] = lstCreateParams;

        address[] memory recipients = new address[](1);
        recipients[0] = address(this);

        ALLOCATION_MANAGER.createRedistributingOperatorSets(address(this), _params, recipients);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol";
import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IAlephVaultDeposit} from "Aleph/src/interfaces/IAlephVaultDeposit.sol";
import {IAlephVaultRedeem} from "Aleph/src/interfaces/IAlephVaultRedeem.sol";
import {IERC20Factory} from "./interfaces/IERC20Factory.sol";

/**
 * @title IAlephAVS
 * @notice Interface for the AlephAVS contract
 */
interface IAlephAVS is IAVSRegistrar {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when an operator registers for AlephAVS operator sets
     * @param operator The operator address
     * @param operatorSetIds The operator set IDs the operator registered for
     */
    event OperatorRegistered(address indexed operator, uint32[] operatorSetIds);

    /**
     * @notice Emitted when an operator deregisters from AlephAVS operator sets
     * @param operator The operator address
     * @param operatorSetIds The operator set IDs the operator deregistered from
     */
    event OperatorDeregistered(address indexed operator, uint32[] operatorSetIds);

    /**
     * @notice Emitted when an operator is slashed during allocation
     * @param operator The operator that was slashed
     * @param operatorSetId The operator set ID where the slash occurred
     * @param strategy The strategy that was slashed
     * @param underlyingToken The underlying token of the slashed strategy
     * @param amount The amount of tokens received from the slash
     * @param slashId The unique identifier for this slash event
     */
    event SlashExecuted(
        address indexed operator,
        uint32 operatorSetId,
        address indexed strategy,
        address indexed underlyingToken,
        uint256 amount,
        uint256 slashId
    );

    /**
     * @notice Emitted when an operator allocates funds to an Aleph vault
     * @param operator The operator who allocated the funds
     * @param alephVault The Aleph vault address funds were allocated to
     * @param originalStrategy The original LST strategy that was slashed
     * @param slashedStrategy The slashed strategy created for this vault
     * @param tokenAmount The amount of tokens slashed and allocated
     * @param amountToMint The amount of slashed tokens minted and distributed
     * @param vaultShares The vault shares received from the deposit
     * @param classId The share class ID used for the vault deposit
     */
    event AllocatedToAlephVault(
        address indexed operator,
        address indexed alephVault,
        address originalStrategy,
        address slashedStrategy,
        uint256 tokenAmount,
        uint256 amountToMint,
        uint256 vaultShares,
        uint8 classId
    );

    /**
     * @notice Emitted when a user requests to unallocate funds from an Aleph vault
     * @param tokenHolder The address that held the slashed tokens and requested unallocation
     * @param alephVault The Aleph vault address funds are being unallocated from
     * @param slashedStrategy The slashed strategy whose tokens were burned
     * @param tokenAmount The amount of slashed tokens burned
     * @param estAmountToRedeem The estimated amount that will be redeemed from the vault
     * @param batchId The batch ID for the redeem request
     * @param classId The share class ID used for the vault redemption
     */
    event UnallocateRequested(
        address indexed tokenHolder,
        address indexed alephVault,
        address slashedStrategy,
        uint256 tokenAmount,
        uint256 estAmountToRedeem,
        uint48 batchId,
        uint8 classId
    );

    /**
     * @notice Emitted when funds are unallocated from an Aleph vault
     * @param tokenHolder The address that held the slashed tokens and called unallocate
     * @param alephVault The Aleph vault address funds were unallocated from
     * @param originalStrategy The original LST strategy where tokens were deposited back
     * @param slashedStrategy The slashed strategy whose tokens were burned
     * @param amount The amount of tokens redeemed from the vault and deposited to the strategy
     * @param shares The shares received in the original LST strategy
     * @param classId The share class ID used for the vault redemption
     */
    event UnallocateCompleted(
        address indexed tokenHolder,
        address indexed alephVault,
        address originalStrategy,
        address slashedStrategy,
        uint256 amount,
        uint256 shares,
        uint8 classId
    );

    /**
     * @notice Emitted when a vault is initialized with its slashed token and strategy
     * @param vault The vault address that was initialized
     * @param originalToken The original token (vault's underlying token)
     * @param slashedToken The slashed token created for this vault
     * @param slashedStrategy The slashed strategy created for this vault
     */
    event VaultInitialized(
        address indexed vault,
        uint8 classId,
        address indexed originalToken,
        address indexed slashedToken,
        address slashedStrategy
    );

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error NotRegisteredOperator();
    error InvalidAllocationManager();
    error Unauthorized();
    error InsufficientAllocation();
    error InvalidOperatorSet();
    error InvalidStrategyManager();
    error InvalidStrategy();
    error InvalidDelegationManager();
    error InvalidRewardsCoordinator();
    error InvalidVaultFactory();
    error InvalidVault();
    error InvalidStrategyFactory();
    error NotMemberOfOperatorSet();
    error AmountTooSmall();
    error MagnitudeOverflow();
    error InvalidAmount();
    error InsufficientBalance();
    error TokenAmountMismatch();
    error NotAlephOperator();
    error NoTokensReceived();
    error InvalidAlephOperator();
    error OperatorSplitNotZero();
    error InvalidClassId();
    error VaultAlreadyInitialized();
    error InsufficientOutput(uint256 actualAmount, uint256 minAmount);
    error VaultNotInitialized(address vault);
    error NoPendingUnallocation();

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Returns the ERC20 factory contract
     * @return The ERC20 factory contract address
     */
    function erc20Factory() external view returns (IERC20Factory);

    /**
     * @notice Returns the slashed strategy for a given vault
     * @param vault The vault address
     * @return The slashed strategy for the vault
     */
    function vaultToSlashedStrategy(address vault) external view returns (IStrategy);

    function vaultToOriginalStrategy(address vault) external view returns (IStrategy);

    /*//////////////////////////////////////////////////////////////
                            PUBLIC FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Allocates funds by slashing the operator, depositing to Aleph vault, and creating rewards submission
     * @param _alephVault The Aleph vault address to deposit to
     * @param _requestDepositParams Parameters for vault deposit (amount, authSignature, classId)
     */
    function allocate(address _alephVault, IAlephVaultDeposit.RequestDepositParams calldata _requestDepositParams)
        external;

    /**
     * @notice Requests to unallocate funds by redeeming slashed tokens from vault
     * @dev This is the first step of the two-step unallocate flow. Called by anyone who holds slashed strategy tokens.
     *
     * **How it works:**
     * 1. Burns the slashed tokens from the caller
     * 2. Calculates the estimated amount to redeem from the vault (based on current vault price per share)
     * 3. Requests redemption from the vault (the vault sees only the AVS as the allocator)
     * 4. Stores the estimated amount as pending unallocation for the caller
     *
     * **Important Notes:**
     * - The caller must hold at least `_tokenAmount` of slashed strategy tokens
     * - The estimated amount may differ from the actual amount redeemed due to vault price changes
     * - Multiple users can request unallocation; funds are distributed proportionally when available
     * - After calling this, the user must wait for the vault to process the redemption request
     * - Use `getPendingUnallocateStatus()` to check when `completeUnallocate()` can be called
     *
     * **Edge Cases:**
     * - If `_tokenAmount` is 0, reverts with `InvalidAmount`
     * - If vault is not initialized, reverts with `VaultNotInitialized`
     * - If calculated `estAmountToRedeem` is 0, reverts with `InvalidAmount`
     *
     * @param _alephVault The Aleph vault address to unallocate from (must be initialized)
     * @param _tokenAmount The amount of slashed strategy tokens to unallocate (must be > 0)
     * @return batchId The batch ID for the redeem request (used by the vault for tracking)
     * @return estAmountToRedeem The estimated amount that will be redeemed from the vault
     *
     * @custom:example
     * ```solidity
     * // User has 100 slashed tokens and wants to unallocate
     * (uint48 batchId, uint256 estAmount) = alephAVS.requestUnallocate(vaultAddress, 100e18);
     * // estAmount might be 95e18 if vault price per share is 0.95
     * ```
     */
    function requestUnallocate(address _alephVault, uint256 _tokenAmount)
        external
        returns (uint48 batchId, uint256 estAmountToRedeem);

    /**
     * @notice Completes the unallocation by withdrawing redeemable amount and depositing back to strategy
     * @dev This is the second step of the two-step unallocate flow. Called by the user who previously called `requestUnallocate`.
     *
     * **How it works:**
     * 1. Checks that the user has pending unallocation (from `requestUnallocate`)
     * 2. Calculates the expected amount based on proportional distribution (if multiple users have pending)
     * 3. Withdraws redeemable amount from the vault (if available)
     * 4. Caps the amount to actual available balances (vault redeemable + previously withdrawn)
     * 5. Deposits the amount back into the original LST strategy using the provided signature
     * 6. Clears the user's pending unallocation
     *
     * **Proportional Distribution:**
     * If multiple users have requested unallocation, the available funds are distributed proportionally:
     * - User's share = (userPendingAmount / totalPendingAmount) * totalRedeemableAmount
     * - If this is the last user, they receive all remaining withdrawn amount
     *
     * **Signature Requirements:**
     * - The signature must be generated using `calculateCompleteUnallocateAmount()` to get the exact expected amount
     * - Signature is for `depositIntoStrategyWithSignature()` in StrategyManager
     * - Must be signed by the caller (msg.sender) with the correct nonce
     * - Expiry must be in the future
     *
     * **Important Notes:**
     * - Call `getPendingUnallocateStatus()` first to check if unallocation can be completed
     * - Call `calculateCompleteUnallocateAmount()` to get the expected amount for signature generation
     * - The actual amount may be slightly less than expected due to rounding or insufficient funds
     * - If vault has no redeemable amount yet, wait for the vault to process the redemption request
     *
     * **Edge Cases:**
     * - If user has no pending unallocation, reverts with `NoPendingUnallocation`
     * - If vault has no redeemable amount and no previously withdrawn amount, reverts with `InvalidAmount`
     * - If signature is invalid or expired, the strategy deposit will fail
     * - If calculated amount is 0, reverts with `InvalidAmount`
     *
     * @param _alephVault The Aleph vault address to complete unallocation from (must be initialized)
     * @param _strategyDepositExpiry The expiry timestamp for the strategy deposit signature (must be in future)
     * @param _strategyDepositSignature The caller's EIP-712 signature for depositing back into the original LST strategy
     * @return amount The amount of tokens redeemed from the vault and deposited to the strategy
     * @return shares The shares received in the original LST strategy
     *
     * @custom:example
     * ```solidity
     * // First, check status and get expected amount
     * uint256 expectedAmount = alephAVS.calculateCompleteUnallocateAmount(userAddress, vaultAddress);
     * // Generate signature with expectedAmount
     * bytes memory sig = generateStrategyDepositSignature(expectedAmount, nonce, expiry);
     * // Complete unallocation
     * (uint256 amount, uint256 shares) = alephAVS.completeUnallocate(vaultAddress, expiry, sig);
     * ```
     */
    function completeUnallocate(
        address _alephVault,
        uint256 _strategyDepositExpiry,
        bytes calldata _strategyDepositSignature
    ) external returns (uint256 amount, uint256 shares);

    /**
     * @notice Calculates the estimated amount that will be redeemed from the vault for unallocation
     * @dev This view function allows callers to calculate the amount before calling unallocate,
     *      so they can create the signature for depositIntoStrategyWithSignature
     * @param _alephVault The Aleph vault address to unallocate from
     * @param _tokenAmount The amount of slashed strategy tokens to unallocate
     * @return estimatedAmount The estimated amount of tokens that will be redeemed from the vault
     * @return strategy The original LST strategy where tokens will be deposited
     * @return token The vault token that will be deposited to the strategy
     */
    function calculateUnallocateAmount(address _alephVault, uint256 _tokenAmount)
        external
        view
        returns (uint256 estimatedAmount, IStrategy strategy, IERC20 token);

    /**
     * @notice Returns the pending unallocation status for a user and vault
     * @dev This view function allows users to check if they can call completeUnallocate
     * @param _user The user address to check
     * @param _alephVault The Aleph vault address
     * @return userPendingAmount The user's pending unallocation amount
     * @return totalPendingAmount The total pending unallocation amount for the vault
     * @return redeemableAmount The amount currently redeemable from the vault
     * @return canComplete Whether the user can complete unallocation (has pending amount and vault has redeemable amount)
     */
    function getPendingUnallocateStatus(address _user, address _alephVault)
        external
        view
        returns (uint256 userPendingAmount, uint256 totalPendingAmount, uint256 redeemableAmount, bool canComplete);

    /**
     * @notice Calculates the expected amount that will be withdrawn in completeUnallocate
     * @dev This view function allows users to calculate the exact amount before calling `completeUnallocate`,
     *      so they can generate the strategy deposit signature with the correct amount.
     *
     * **Use this function to:**
     * 1. Get the expected amount for signature generation
     * 2. Verify if unallocation can be completed
     * 3. Check the amount before calling `completeUnallocate`
     *
     * **Calculation Logic:**
     * - If user is the only one with pending unallocation: gets all available redeemable amount
     * - If multiple users have pending: proportional distribution based on their pending amounts
     * - Formula: (userPendingAmount / totalPendingAmount) * totalRedeemableAmount
     * - Total redeemable = vault redeemable amount + previously withdrawn amount
     *
     * **Important:**
     * - This is a view function - no state changes
     * - The actual amount in `completeUnallocate` may be slightly different due to:
     *   - Rounding differences
     *   - Changes in vault redeemable amount between calls
     *   - Contract balance constraints
     * - Always use this amount for signature generation to ensure it matches
     *
     * @param _user The user address to calculate for
     * @param _alephVault The Aleph vault address
     * @return expectedAmount The expected amount that will be withdrawn and deposited to strategy (0 if no pending or no redeemable)
     *
     * @custom:example
     * ```solidity
     * uint256 expected = alephAVS.calculateCompleteUnallocateAmount(userAddress, vaultAddress);
     * if (expected > 0) {
     *     // Generate signature with expected amount
     *     bytes memory sig = generateSignature(expected, nonce, expiry);
     *     // Then call completeUnallocate
     * }
     * ```
     */
    function calculateCompleteUnallocateAmount(address _user, address _alephVault)
        external
        view
        returns (uint256 expectedAmount);

    /**
     * @notice Initializes a vault by creating its slashed token and strategy
     * @param _classId The share class ID for the vault
     * @param _vault The vault address to initialize
     * @param _lstStrategy The original LST strategy for the vault's underlying token
     * @return slashedStrategy The created slashed strategy address
     */
    function initializeVault(uint8 _classId, address _vault, IStrategy _lstStrategy)
        external
        returns (IStrategy slashedStrategy);
}

File 3 of 81 : IAllocationManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import {OperatorSet} from "../libraries/OperatorSetLib.sol";
import "./IPauserRegistry.sol";
import "./IStrategy.sol";
import "./IAVSRegistrar.sol";
import "./ISemVerMixin.sol";

interface IAllocationManagerErrors {
    /// Input Validation

    /// @dev Thrown when `wadToSlash` is zero or greater than 1e18
    error InvalidWadToSlash();
    /// @dev Thrown when two array parameters have mismatching lengths.
    error InputArrayLengthMismatch();
    /// @dev Thrown when the AVSRegistrar is not correctly configured to prevent an AVSRegistrar contract
    /// from being used with the wrong AVS
    error InvalidAVSRegistrar();
    /// @dev Thrown when an invalid strategy is provided.
    error InvalidStrategy();
    /// @dev Thrown when an invalid redistribution recipient is provided.
    error InvalidRedistributionRecipient();

    /// Caller

    /// @dev Thrown when caller is not authorized to call a function.
    error InvalidCaller();

    /// Operator Status

    /// @dev Thrown when an invalid operator is provided.
    error InvalidOperator();
    /// @dev Thrown when an invalid avs whose metadata is not registered is provided.
    error NonexistentAVSMetadata();
    /// @dev Thrown when an operator's allocation delay has yet to be set.
    error UninitializedAllocationDelay();
    /// @dev Thrown when attempting to slash an operator when they are not slashable.
    error OperatorNotSlashable();
    /// @dev Thrown when trying to add an operator to a set they are already a member of
    error AlreadyMemberOfSet();
    /// @dev Thrown when trying to slash/remove an operator from a set they are not a member of
    error NotMemberOfSet();

    /// Operator Set Status

    /// @dev Thrown when an invalid operator set is provided.
    error InvalidOperatorSet();
    /// @dev Thrown when provided `strategies` are not in ascending order.
    error StrategiesMustBeInAscendingOrder();
    /// @dev Thrown when trying to add a strategy to an operator set that already contains it.
    error StrategyAlreadyInOperatorSet();
    /// @dev Thrown when a strategy is referenced that does not belong to an operator set.
    error StrategyNotInOperatorSet();

    /// Modifying Allocations

    /// @dev Thrown when an operator attempts to set their allocation for an operatorSet to the same value
    error SameMagnitude();
    /// @dev Thrown when an allocation is attempted for a given operator when they have pending allocations or deallocations.
    error ModificationAlreadyPending();
    /// @dev Thrown when an allocation is attempted that exceeds a given operators total allocatable magnitude.
    error InsufficientMagnitude();
}

interface IAllocationManagerTypes {
    /**
     * @notice Defines allocation information from a strategy to an operator set, for an operator
     * @param currentMagnitude the current magnitude allocated from the strategy to the operator set
     * @param pendingDiff a pending change in magnitude, if it exists (0 otherwise)
     * @param effectBlock the block at which the pending magnitude diff will take effect
     */
    struct Allocation {
        uint64 currentMagnitude;
        int128 pendingDiff;
        uint32 effectBlock;
    }

    /**
     * @notice Struct containing allocation delay metadata for a given operator.
     * @param delay Current allocation delay
     * @param isSet Whether the operator has initially set an allocation delay. Note that this could be false but the
     * block.number >= effectBlock in which we consider their delay to be configured and active.
     * @param pendingDelay The delay that will take effect after `effectBlock`
     * @param effectBlock The block number after which a pending delay will take effect
     */
    struct AllocationDelayInfo {
        uint32 delay;
        bool isSet;
        uint32 pendingDelay;
        uint32 effectBlock;
    }

    /**
     * @notice Contains registration details for an operator pertaining to an operator set
     * @param registered Whether the operator is currently registered for the operator set
     * @param slashableUntil If the operator is not registered, they are still slashable until
     * this block is reached.
     */
    struct RegistrationStatus {
        bool registered;
        uint32 slashableUntil;
    }

    /**
     * @notice Contains allocation info for a specific strategy
     * @param maxMagnitude the maximum magnitude that can be allocated between all operator sets
     * @param encumberedMagnitude the currently-allocated magnitude for the strategy
     */
    struct StrategyInfo {
        uint64 maxMagnitude;
        uint64 encumberedMagnitude;
    }

    /**
     * @notice Struct containing parameters to slashing
     * @param operator the address to slash
     * @param operatorSetId the ID of the operatorSet the operator is being slashed on behalf of
     * @param strategies the set of strategies to slash
     * @param wadsToSlash the parts in 1e18 to slash, this will be proportional to the operator's
     * slashable stake allocation for the operatorSet
     * @param description the description of the slashing provided by the AVS for legibility
     */
    struct SlashingParams {
        address operator;
        uint32 operatorSetId;
        IStrategy[] strategies;
        uint256[] wadsToSlash;
        string description;
    }

    /**
     * @notice struct used to modify the allocation of slashable magnitude to an operator set
     * @param operatorSet the operator set to modify the allocation for
     * @param strategies the strategies to modify allocations for
     * @param newMagnitudes the new magnitude to allocate for each strategy to this operator set
     */
    struct AllocateParams {
        OperatorSet operatorSet;
        IStrategy[] strategies;
        uint64[] newMagnitudes;
    }

    /**
     * @notice Parameters used to register for an AVS's operator sets
     * @param avs the AVS being registered for
     * @param operatorSetIds the operator sets within the AVS to register for
     * @param data extra data to be passed to the AVS to complete registration
     */
    struct RegisterParams {
        address avs;
        uint32[] operatorSetIds;
        bytes data;
    }

    /**
     * @notice Parameters used to deregister from an AVS's operator sets
     * @param operator the operator being deregistered
     * @param avs the avs being deregistered from
     * @param operatorSetIds the operator sets within the AVS being deregistered from
     */
    struct DeregisterParams {
        address operator;
        address avs;
        uint32[] operatorSetIds;
    }

    /**
     * @notice Parameters used by an AVS to create new operator sets
     * @param operatorSetId the id of the operator set to create
     * @param strategies the strategies to add as slashable to the operator set
     */
    struct CreateSetParams {
        uint32 operatorSetId;
        IStrategy[] strategies;
    }
}

interface IAllocationManagerEvents is IAllocationManagerTypes {
    /// @notice Emitted when operator updates their allocation delay.
    event AllocationDelaySet(address operator, uint32 delay, uint32 effectBlock);

    /// @notice Emitted when an operator's magnitude is updated for a given operatorSet and strategy
    event AllocationUpdated(
        address operator, OperatorSet operatorSet, IStrategy strategy, uint64 magnitude, uint32 effectBlock
    );

    /// @notice Emitted when operator's encumbered magnitude is updated for a given strategy
    event EncumberedMagnitudeUpdated(address operator, IStrategy strategy, uint64 encumberedMagnitude);

    /// @notice Emitted when an operator's max magnitude is updated for a given strategy
    event MaxMagnitudeUpdated(address operator, IStrategy strategy, uint64 maxMagnitude);

    /// @notice Emitted when an operator is slashed by an operator set for a strategy
    /// `wadSlashed` is the proportion of the operator's total delegated stake that was slashed
    event OperatorSlashed(
        address operator, OperatorSet operatorSet, IStrategy[] strategies, uint256[] wadSlashed, string description
    );

    /// @notice Emitted when an AVS configures the address that will handle registration/deregistration
    event AVSRegistrarSet(address avs, IAVSRegistrar registrar);

    /// @notice Emitted when an AVS updates their metadata URI (Uniform Resource Identifier).
    /// @dev The URI is never stored; it is simply emitted through an event for off-chain indexing.
    event AVSMetadataURIUpdated(address indexed avs, string metadataURI);

    /// @notice Emitted when an operator set is created by an AVS.
    event OperatorSetCreated(OperatorSet operatorSet);

    /// @notice Emitted when an operator is added to an operator set.
    event OperatorAddedToOperatorSet(address indexed operator, OperatorSet operatorSet);

    /// @notice Emitted when an operator is removed from an operator set.
    event OperatorRemovedFromOperatorSet(address indexed operator, OperatorSet operatorSet);

    /// @notice Emitted when a redistributing operator set is created by an AVS.
    event RedistributionAddressSet(OperatorSet operatorSet, address redistributionRecipient);

    /// @notice Emitted when a strategy is added to an operator set.
    event StrategyAddedToOperatorSet(OperatorSet operatorSet, IStrategy strategy);

    /// @notice Emitted when a strategy is removed from an operator set.
    event StrategyRemovedFromOperatorSet(OperatorSet operatorSet, IStrategy strategy);
}

interface IAllocationManager is IAllocationManagerErrors, IAllocationManagerEvents, ISemVerMixin {
    /**
     * @dev Initializes the initial owner and paused status.
     */
    function initialize(
        uint256 initialPausedStatus
    ) external;

    /**
     * @notice Called by an AVS to slash an operator in a given operator set. The operator must be registered
     * and have slashable stake allocated to the operator set.
     *
     * @param avs The AVS address initiating the slash.
     * @param params The slashing parameters, containing:
     *  - operator: The operator to slash.
     *  - operatorSetId: The ID of the operator set the operator is being slashed from.
     *  - strategies: Array of strategies to slash allocations from (must be in ascending order).
     *  - wadsToSlash: Array of proportions to slash from each strategy (must be between 0 and 1e18).
     *  - description: Description of why the operator was slashed.
     *
     * @return slashId The ID of the slash.
     * @return shares The amount of shares that were slashed for each strategy.
     *
     * @dev For each strategy:
     *      1. Reduces the operator's current allocation magnitude by wadToSlash proportion.
     *      2. Reduces the strategy's max and encumbered magnitudes proportionally.
     *      3. If there is a pending deallocation, reduces it proportionally.
     *      4. Updates the operator's shares in the DelegationManager.
     *
     * @dev Small slashing amounts may not result in actual token burns due to
     *      rounding, which will result in small amounts of tokens locked in the contract
     *      rather than fully burning through the burn mechanism.
     */
    function slashOperator(
        address avs,
        SlashingParams calldata params
    ) external returns (uint256 slashId, uint256[] memory shares);

    /**
     * @notice Modifies the proportions of slashable stake allocated to an operator set from a list of strategies
     * Note that deallocations remain slashable for DEALLOCATION_DELAY blocks therefore when they are cleared they may
     * free up less allocatable magnitude than initially deallocated.
     * @param operator the operator to modify allocations for
     * @param params array of magnitude adjustments for one or more operator sets
     * @dev Updates encumberedMagnitude for the updated strategies
     */
    function modifyAllocations(address operator, AllocateParams[] calldata params) external;

    /**
     * @notice This function takes a list of strategies and for each strategy, removes from the deallocationQueue
     * all clearable deallocations up to max `numToClear` number of deallocations, updating the encumberedMagnitude
     * of the operator as needed.
     *
     * @param operator address to clear deallocations for
     * @param strategies a list of strategies to clear deallocations for
     * @param numToClear a list of number of pending deallocations to clear for each strategy
     *
     * @dev can be called permissionlessly by anyone
     */
    function clearDeallocationQueue(
        address operator,
        IStrategy[] calldata strategies,
        uint16[] calldata numToClear
    ) external;

    /**
     * @notice Allows an operator to register for one or more operator sets for an AVS. If the operator
     * has any stake allocated to these operator sets, it immediately becomes slashable.
     * @dev After registering within the ALM, this method calls the AVS Registrar's `IAVSRegistrar.
     * registerOperator` method to complete registration. This call MUST succeed in order for
     * registration to be successful.
     */
    function registerForOperatorSets(address operator, RegisterParams calldata params) external;

    /**
     * @notice Allows an operator or AVS to deregister the operator from one or more of the AVS's operator sets.
     * If the operator has any slashable stake allocated to the AVS, it remains slashable until the
     * DEALLOCATION_DELAY has passed.
     * @dev After deregistering within the ALM, this method calls the AVS Registrar's `IAVSRegistrar.
     * deregisterOperator` method to complete deregistration. This call MUST succeed in order for
     * deregistration to be successful.
     */
    function deregisterFromOperatorSets(
        DeregisterParams calldata params
    ) external;

    /**
     * @notice Called by the delegation manager OR an operator to set an operator's allocation delay.
     * This is set when the operator first registers, and is the number of blocks between an operator
     * allocating magnitude to an operator set, and the magnitude becoming slashable.
     * @param operator The operator to set the delay on behalf of.
     * @param delay the allocation delay in blocks
     */
    function setAllocationDelay(address operator, uint32 delay) external;

    /**
     * @notice Called by an AVS to configure the address that is called when an operator registers
     * or is deregistered from the AVS's operator sets. If not set (or set to 0), defaults
     * to the AVS's address.
     * @param registrar the new registrar address
     */
    function setAVSRegistrar(address avs, IAVSRegistrar registrar) external;

    /**
     *  @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
     *
     *  @param metadataURI The URI for metadata associated with an AVS.
     *
     *  @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
     */
    function updateAVSMetadataURI(address avs, string calldata metadataURI) external;

    /**
     * @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses
     */
    function createOperatorSets(address avs, CreateSetParams[] calldata params) external;

    /**
     * @notice Allows an AVS to create new Redistribution operator sets.
     * @param avs The AVS creating the new operator sets.
     * @param params An array of operator set creation parameters.
     * @param redistributionRecipients An array of addresses that will receive redistributed funds when operators are slashed.
     * @dev Same logic as `createOperatorSets`, except `redistributionRecipients` corresponding to each operator set are stored.
     *      Additionally, emits `RedistributionOperatorSetCreated` event instead of `OperatorSetCreated` for each created operator set.
     */
    function createRedistributingOperatorSets(
        address avs,
        CreateSetParams[] calldata params,
        address[] calldata redistributionRecipients
    ) external;

    /**
     * @notice Allows an AVS to add strategies to an operator set
     * @dev Strategies MUST NOT already exist in the operator set
     * @dev If the operatorSet is redistributing, the `BEACONCHAIN_ETH_STRAT` may not be added, since redistribution is not supported for native eth
     * @param avs the avs to set strategies for
     * @param operatorSetId the operator set to add strategies to
     * @param strategies the strategies to add
     */
    function addStrategiesToOperatorSet(address avs, uint32 operatorSetId, IStrategy[] calldata strategies) external;

    /**
     * @notice Allows an AVS to remove strategies from an operator set
     * @dev Strategies MUST already exist in the operator set
     * @param avs the avs to remove strategies for
     * @param operatorSetId the operator set to remove strategies from
     * @param strategies the strategies to remove
     */
    function removeStrategiesFromOperatorSet(
        address avs,
        uint32 operatorSetId,
        IStrategy[] calldata strategies
    ) external;

    /**
     *
     *                         VIEW FUNCTIONS
     *
     */

    /**
     * @notice Returns the number of operator sets for the AVS
     * @param avs the AVS to query
     */
    function getOperatorSetCount(
        address avs
    ) external view returns (uint256);

    /**
     * @notice Returns the list of operator sets the operator has current or pending allocations/deallocations in
     * @param operator the operator to query
     * @return the list of operator sets the operator has current or pending allocations/deallocations in
     */
    function getAllocatedSets(
        address operator
    ) external view returns (OperatorSet[] memory);

    /**
     * @notice Returns the list of strategies an operator has current or pending allocations/deallocations from
     * given a specific operator set.
     * @param operator the operator to query
     * @param operatorSet the operator set to query
     * @return the list of strategies
     */
    function getAllocatedStrategies(
        address operator,
        OperatorSet memory operatorSet
    ) external view returns (IStrategy[] memory);

    /**
     * @notice Returns the current/pending stake allocation an operator has from a strategy to an operator set
     * @param operator the operator to query
     * @param operatorSet the operator set to query
     * @param strategy the strategy to query
     * @return the current/pending stake allocation
     */
    function getAllocation(
        address operator,
        OperatorSet memory operatorSet,
        IStrategy strategy
    ) external view returns (Allocation memory);

    /**
     * @notice Returns the current/pending stake allocations for multiple operators from a strategy to an operator set
     * @param operators the operators to query
     * @param operatorSet the operator set to query
     * @param strategy the strategy to query
     * @return each operator's allocation
     */
    function getAllocations(
        address[] memory operators,
        OperatorSet memory operatorSet,
        IStrategy strategy
    ) external view returns (Allocation[] memory);

    /**
     * @notice Given a strategy, returns a list of operator sets and corresponding stake allocations.
     * @dev Note that this returns a list of ALL operator sets the operator has allocations in. This means
     * some of the returned allocations may be zero.
     * @param operator the operator to query
     * @param strategy the strategy to query
     * @return the list of all operator sets the operator has allocations for
     * @return the corresponding list of allocations from the specific `strategy`
     */
    function getStrategyAllocations(
        address operator,
        IStrategy strategy
    ) external view returns (OperatorSet[] memory, Allocation[] memory);

    /**
     * @notice For a strategy, get the amount of magnitude that is allocated across one or more operator sets
     * @param operator the operator to query
     * @param strategy the strategy to get allocatable magnitude for
     * @return currently allocated magnitude
     */
    function getEncumberedMagnitude(address operator, IStrategy strategy) external view returns (uint64);

    /**
     * @notice For a strategy, get the amount of magnitude not currently allocated to any operator set
     * @param operator the operator to query
     * @param strategy the strategy to get allocatable magnitude for
     * @return magnitude available to be allocated to an operator set
     */
    function getAllocatableMagnitude(address operator, IStrategy strategy) external view returns (uint64);

    /**
     * @notice Returns the maximum magnitude an operator can allocate for the given strategy
     * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime
     * the operator is slashed. This value acts as a cap on the max magnitude of the operator.
     * @param operator the operator to query
     * @param strategy the strategy to get the max magnitude for
     * @return the max magnitude for the strategy
     */
    function getMaxMagnitude(address operator, IStrategy strategy) external view returns (uint64);

    /**
     * @notice Returns the maximum magnitude an operator can allocate for the given strategies
     * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime
     * the operator is slashed. This value acts as a cap on the max magnitude of the operator.
     * @param operator the operator to query
     * @param strategies the strategies to get the max magnitudes for
     * @return the max magnitudes for each strategy
     */
    function getMaxMagnitudes(
        address operator,
        IStrategy[] calldata strategies
    ) external view returns (uint64[] memory);

    /**
     * @notice Returns the maximum magnitudes each operator can allocate for the given strategy
     * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime
     * the operator is slashed. This value acts as a cap on the max magnitude of the operator.
     * @param operators the operators to query
     * @param strategy the strategy to get the max magnitudes for
     * @return the max magnitudes for each operator
     */
    function getMaxMagnitudes(
        address[] calldata operators,
        IStrategy strategy
    ) external view returns (uint64[] memory);

    /**
     * @notice Returns the maximum magnitude an operator can allocate for the given strategies
     * at a given block number
     * @dev The max magnitude of an operator starts at WAD (1e18), and is decreased anytime
     * the operator is slashed. This value acts as a cap on the max magnitude of the operator.
     * @param operator the operator to query
     * @param strategies the strategies to get the max magnitudes for
     * @param blockNumber the blockNumber at which to check the max magnitudes
     * @return the max magnitudes for each strategy
     */
    function getMaxMagnitudesAtBlock(
        address operator,
        IStrategy[] calldata strategies,
        uint32 blockNumber
    ) external view returns (uint64[] memory);

    /**
     * @notice Returns the time in blocks between an operator allocating slashable magnitude
     * and the magnitude becoming slashable. If the delay has not been set, `isSet` will be false.
     * @dev The operator must have a configured delay before allocating magnitude
     * @param operator The operator to query
     * @return isSet Whether the operator has configured a delay
     * @return delay The time in blocks between allocating magnitude and magnitude becoming slashable
     */
    function getAllocationDelay(
        address operator
    ) external view returns (bool isSet, uint32 delay);

    /**
     * @notice Returns the number of blocks between an operator deallocating magnitude and the magnitude becoming
     * unslashable and then being able to be reallocated to another operator set. Note that unlike the allocation delay
     * which is configurable by the operator, the DEALLOCATION_DELAY is globally fixed and cannot be changed.
     */
    function DEALLOCATION_DELAY() external view returns (uint32 delay);

    /**
     * @notice Returns a list of all operator sets the operator is registered for
     * @param operator The operator address to query.
     */
    function getRegisteredSets(
        address operator
    ) external view returns (OperatorSet[] memory operatorSets);

    /**
     * @notice Returns whether the operator is registered for the operator set
     * @param operator The operator to query
     * @param operatorSet The operator set to query
     */
    function isMemberOfOperatorSet(address operator, OperatorSet memory operatorSet) external view returns (bool);

    /**
     * @notice Returns whether the operator set exists
     */
    function isOperatorSet(
        OperatorSet memory operatorSet
    ) external view returns (bool);

    /**
     * @notice Returns all the operators registered to an operator set
     * @param operatorSet The operatorSet to query.
     */
    function getMembers(
        OperatorSet memory operatorSet
    ) external view returns (address[] memory operators);

    /**
     * @notice Returns the number of operators registered to an operatorSet.
     * @param operatorSet The operatorSet to get the member count for
     */
    function getMemberCount(
        OperatorSet memory operatorSet
    ) external view returns (uint256);

    /**
     * @notice Returns the address that handles registration/deregistration for the AVS
     * If not set, defaults to the input address (`avs`)
     */
    function getAVSRegistrar(
        address avs
    ) external view returns (IAVSRegistrar);

    /**
     * @notice Returns an array of strategies in the operatorSet.
     * @param operatorSet The operatorSet to query.
     */
    function getStrategiesInOperatorSet(
        OperatorSet memory operatorSet
    ) external view returns (IStrategy[] memory strategies);

    /**
     * @notice Returns the minimum amount of stake that will be slashable as of some future block,
     * according to each operator's allocation from each strategy to the operator set. Note that this function
     * will return 0 for the slashable stake if the operator is not slashable at the time of the call.
     * @dev This method queries actual delegated stakes in the DelegationManager and applies
     * each operator's allocation to the stake to produce the slashable stake each allocation
     * represents. This method does not consider slashable stake in the withdrawal queue even though there could be
     * slashable stake in the queue.
     * @dev This minimum takes into account `futureBlock`, and will omit any pending magnitude
     * diffs that will not be in effect as of `futureBlock`. NOTE that in order to get the true
     * minimum slashable stake as of some future block, `futureBlock` MUST be greater than block.number
     * @dev NOTE that `futureBlock` should be fewer than `DEALLOCATION_DELAY` blocks in the future,
     * or the values returned from this method may not be accurate due to deallocations.
     * @param operatorSet the operator set to query
     * @param operators the list of operators whose slashable stakes will be returned
     * @param strategies the strategies that each slashable stake corresponds to
     * @param futureBlock the block at which to get allocation information. Should be a future block.
     */
    function getMinimumSlashableStake(
        OperatorSet memory operatorSet,
        address[] memory operators,
        IStrategy[] memory strategies,
        uint32 futureBlock
    ) external view returns (uint256[][] memory slashableStake);

    /**
     * @notice Returns the current allocated stake, irrespective of the operator's slashable status for the operatorSet.
     * @param operatorSet the operator set to query
     * @param operators the operators to query
     * @param strategies the strategies to query
     */
    function getAllocatedStake(
        OperatorSet memory operatorSet,
        address[] memory operators,
        IStrategy[] memory strategies
    ) external view returns (uint256[][] memory slashableStake);

    /**
     * @notice Returns whether an operator is slashable by an operator set.
     * This returns true if the operator is registered or their slashableUntil block has not passed.
     * This is because even when operators are deregistered, they still remain slashable for a period of time.
     * @param operator the operator to check slashability for
     * @param operatorSet the operator set to check slashability for
     */
    function isOperatorSlashable(address operator, OperatorSet memory operatorSet) external view returns (bool);

    /**
     * @notice Returns the address where slashed funds will be sent for a given operator set.
     * @param operatorSet The Operator Set to query.
     * @return For redistributing Operator Sets, returns the configured redistribution address set during Operator Set creation.
     *         For non-redistributing operator sets, returns the `DEFAULT_BURN_ADDRESS`.
     */
    function getRedistributionRecipient(
        OperatorSet memory operatorSet
    ) external view returns (address);

    /**
     * @notice Returns whether a given operator set supports redistribution
     * or not when funds are slashed and burned from EigenLayer.
     * @param operatorSet The Operator Set to query.
     * @return For redistributing Operator Sets, returns true.
     *         For non-redistributing Operator Sets, returns false.
     */
    function isRedistributingOperatorSet(
        OperatorSet memory operatorSet
    ) external view returns (bool);

    /**
     * @notice Returns the number of slashes for a given operator set.
     * @param operatorSet The operator set to query.
     * @return The number of slashes for the operator set.
     */
    function getSlashCount(
        OperatorSet memory operatorSet
    ) external view returns (uint256);

    /**
     * @notice Returns whether an operator is slashable by a redistributing operator set.
     * @param operator The operator to query.
     */
    function isOperatorRedistributable(
        address operator
    ) external view returns (bool);
}

File 4 of 81 : IDelegationManager.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "./IStrategy.sol";
import "./IPauserRegistry.sol";
import "./ISignatureUtilsMixin.sol";
import "../libraries/SlashingLib.sol";
import "../libraries/OperatorSetLib.sol";

interface IDelegationManagerErrors {
    /// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract.
    error OnlyStrategyManagerOrEigenPodManager();
    /// @dev Thrown when msg.sender is not the EigenPodManager
    error OnlyEigenPodManager();
    /// @dev Throw when msg.sender is not the AllocationManager
    error OnlyAllocationManager();

    /// Delegation Status

    /// @dev Thrown when an operator attempts to undelegate.
    error OperatorsCannotUndelegate();
    /// @dev Thrown when an account is actively delegated.
    error ActivelyDelegated();
    /// @dev Thrown when an account is not actively delegated.
    error NotActivelyDelegated();
    /// @dev Thrown when `operator` is not a registered operator.
    error OperatorNotRegistered();

    /// Invalid Inputs

    /// @dev Thrown when attempting to execute an action that was not queued.
    error WithdrawalNotQueued();
    /// @dev Thrown when caller cannot undelegate on behalf of a staker.
    error CallerCannotUndelegate();
    /// @dev Thrown when two array parameters have mismatching lengths.
    error InputArrayLengthMismatch();
    /// @dev Thrown when input arrays length is zero.
    error InputArrayLengthZero();

    /// Slashing

    /// @dev Thrown when an operator has been fully slashed(maxMagnitude is 0) for a strategy.
    /// or if the staker has had been natively slashed to the point of their beaconChainScalingFactor equalling 0.
    error FullySlashed();

    /// Signatures

    /// @dev Thrown when attempting to spend a spent eip-712 salt.
    error SaltSpent();

    /// Withdrawal Processing

    /// @dev Thrown when attempting to withdraw before delay has elapsed.
    error WithdrawalDelayNotElapsed();
    /// @dev Thrown when withdrawer is not the current caller.
    error WithdrawerNotCaller();
}

interface IDelegationManagerTypes {
    // @notice Struct used for storing information about a single operator who has registered with EigenLayer
    struct OperatorDetails {
        /// @notice DEPRECATED -- this field is no longer used, payments are handled in RewardsCoordinator.sol
        address __deprecated_earningsReceiver;
        /**
         * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
         * @dev Signature verification follows these rules:
         * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
         * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
         * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
         */
        address delegationApprover;
        /// @notice DEPRECATED -- this field is no longer used. An analogous field is the `allocationDelay` stored in the AllocationManager
        uint32 __deprecated_stakerOptOutWindowBlocks;
    }

    /**
     * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
     * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
     */
    struct DelegationApproval {
        // the staker who is delegating
        address staker;
        // the operator being delegated to
        address operator;
        // the operator's provided salt
        bytes32 salt;
        // the expiration timestamp (UTC) of the signature
        uint256 expiry;
    }

    /**
     * @dev A struct representing an existing queued withdrawal. After the withdrawal delay has elapsed, this withdrawal can be completed via `completeQueuedWithdrawal`.
     * A `Withdrawal` is created by the `DelegationManager` when `queueWithdrawals` is called. The `withdrawalRoots` hashes returned by `queueWithdrawals` can be used
     * to fetch the corresponding `Withdrawal` from storage (via `getQueuedWithdrawal`).
     *
     * @param staker The address that queued the withdrawal
     * @param delegatedTo The address that the staker was delegated to at the time the withdrawal was queued. Used to determine if additional slashing occurred before
     * this withdrawal became completable.
     * @param withdrawer The address that will call the contract to complete the withdrawal. Note that this will always equal `staker`; alternate withdrawers are not
     * supported at this time.
     * @param nonce The staker's `cumulativeWithdrawalsQueued` at time of queuing. Used to ensure withdrawals have unique hashes.
     * @param startBlock The block number when the withdrawal was queued.
     * @param strategies The strategies requested for withdrawal when the withdrawal was queued
     * @param scaledShares The staker's deposit shares requested for withdrawal, scaled by the staker's `depositScalingFactor`. Upon completion, these will be
     * scaled by the appropriate slashing factor as of the withdrawal's completable block. The result is what is actually withdrawable.
     */
    struct Withdrawal {
        address staker;
        address delegatedTo;
        address withdrawer;
        uint256 nonce;
        uint32 startBlock;
        IStrategy[] strategies;
        uint256[] scaledShares;
    }

    /**
     * @param strategies The strategies to withdraw from
     * @param depositShares For each strategy, the number of deposit shares to withdraw. Deposit shares can
     * be queried via `getDepositedShares`.
     * NOTE: The number of shares ultimately received when a withdrawal is completed may be lower depositShares
     * if the staker or their delegated operator has experienced slashing.
     * @param __deprecated_withdrawer This field is ignored. The only party that may complete a withdrawal
     * is the staker that originally queued it. Alternate withdrawers are not supported.
     */
    struct QueuedWithdrawalParams {
        IStrategy[] strategies;
        uint256[] depositShares;
        address __deprecated_withdrawer;
    }
}

interface IDelegationManagerEvents is IDelegationManagerTypes {
    // @notice Emitted when a new operator registers in EigenLayer and provides their delegation approver.
    event OperatorRegistered(address indexed operator, address delegationApprover);

    /// @notice Emitted when an operator updates their delegation approver
    event DelegationApproverUpdated(address indexed operator, address newDelegationApprover);

    /**
     * @notice Emitted when @param operator indicates that they are updating their MetadataURI string
     * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
     */
    event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);

    /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
    event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);

    /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
    event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);

    /// @notice Emitted when @param staker delegates to @param operator.
    event StakerDelegated(address indexed staker, address indexed operator);

    /// @notice Emitted when @param staker undelegates from @param operator.
    event StakerUndelegated(address indexed staker, address indexed operator);

    /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
    event StakerForceUndelegated(address indexed staker, address indexed operator);

    /// @notice Emitted when a staker's depositScalingFactor is updated
    event DepositScalingFactorUpdated(address staker, IStrategy strategy, uint256 newDepositScalingFactor);

    /**
     * @notice Emitted when a new withdrawal is queued.
     * @param withdrawalRoot Is the hash of the `withdrawal`.
     * @param withdrawal Is the withdrawal itself.
     * @param sharesToWithdraw Is an array of the expected shares that were queued for withdrawal corresponding to the strategies in the `withdrawal`.
     */
    event SlashingWithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal, uint256[] sharesToWithdraw);

    /// @notice Emitted when a queued withdrawal is completed
    event SlashingWithdrawalCompleted(bytes32 withdrawalRoot);

    /// @notice Emitted whenever an operator's shares are slashed for a given strategy
    event OperatorSharesSlashed(address indexed operator, IStrategy strategy, uint256 totalSlashedShares);
}

/**
 * @title DelegationManager
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice  This is the contract for delegation in EigenLayer. The main functionalities of this contract are
 * - enabling anyone to register as an operator in EigenLayer
 * - allowing operators to specify parameters related to stakers who delegate to them
 * - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
 * - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
 */
interface IDelegationManager is ISignatureUtilsMixin, IDelegationManagerErrors, IDelegationManagerEvents {
    /**
     * @dev Initializes the initial owner and paused status.
     */
    function initialize(
        uint256 initialPausedStatus
    ) external;

    /**
     * @notice Registers the caller as an operator in EigenLayer.
     * @param initDelegationApprover is an address that, if set, must provide a signature when stakers delegate
     * to an operator.
     * @param allocationDelay The delay before allocations take effect.
     * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
     *
     * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
     * @dev This function will revert if the caller is already delegated to an operator.
     * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
     */
    function registerAsOperator(
        address initDelegationApprover,
        uint32 allocationDelay,
        string calldata metadataURI
    ) external;

    /**
     * @notice Updates an operator's stored `delegationApprover`.
     * @param operator is the operator to update the delegationApprover for
     * @param newDelegationApprover is the new delegationApprover for the operator
     *
     * @dev The caller must have previously registered as an operator in EigenLayer.
     */
    function modifyOperatorDetails(address operator, address newDelegationApprover) external;

    /**
     * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
     * @param operator The operator to update metadata for
     * @param metadataURI The URI for metadata associated with an operator
     * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
     */
    function updateOperatorMetadataURI(address operator, string calldata metadataURI) external;

    /**
     * @notice Caller delegates their stake to an operator.
     * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer.
     * @param approverSignatureAndExpiry (optional) Verifies the operator approves of this delegation
     * @param approverSalt (optional) A unique single use value tied to an individual signature.
     * @dev The signature/salt are used ONLY if the operator has configured a delegationApprover.
     * If they have not, these params can be left empty.
     */
    function delegateTo(
        address operator,
        SignatureWithExpiry memory approverSignatureAndExpiry,
        bytes32 approverSalt
    ) external;

    /**
     * @notice Undelegates the staker from their operator and queues a withdrawal for all of their shares
     * @param staker The account to be undelegated
     * @return withdrawalRoots The roots of the newly queued withdrawals, if a withdrawal was queued. Returns
     * an empty array if none was queued.
     *
     * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
     * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
     * @dev Reverts if the `staker` is not delegated to an operator
     */
    function undelegate(
        address staker
    ) external returns (bytes32[] memory withdrawalRoots);

    /**
     * @notice Undelegates the staker from their current operator, and redelegates to `newOperator`
     * Queues a withdrawal for all of the staker's withdrawable shares. These shares will only be
     * delegated to `newOperator` AFTER the withdrawal is completed.
     * @dev This method acts like a call to `undelegate`, then `delegateTo`
     * @param newOperator the new operator that will be delegated all assets
     * @dev NOTE: the following 2 params are ONLY checked if `newOperator` has a `delegationApprover`.
     * If not, they can be left empty.
     * @param newOperatorApproverSig A signature from the operator's `delegationApprover`
     * @param approverSalt A unique single use value tied to the approver's signature
     */
    function redelegate(
        address newOperator,
        SignatureWithExpiry memory newOperatorApproverSig,
        bytes32 approverSalt
    ) external returns (bytes32[] memory withdrawalRoots);

    /**
     * @notice Allows a staker to queue a withdrawal of their deposit shares. The withdrawal can be
     * completed after the MIN_WITHDRAWAL_DELAY_BLOCKS via either of the completeQueuedWithdrawal methods.
     *
     * While in the queue, these shares are removed from the staker's balance, as well as from their operator's
     * delegated share balance (if applicable). Note that while in the queue, deposit shares are still subject
     * to slashing. If any slashing has occurred, the shares received may be less than the queued deposit shares.
     *
     * @dev To view all the staker's strategies/deposit shares that can be queued for withdrawal, see `getDepositedShares`
     * @dev To view the current conversion between a staker's deposit shares and withdrawable shares, see `getWithdrawableShares`
     */
    function queueWithdrawals(
        QueuedWithdrawalParams[] calldata params
    ) external returns (bytes32[] memory);

    /**
     * @notice Used to complete a queued withdrawal
     * @param withdrawal The withdrawal to complete
     * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array.
     * @param tokens For each `withdrawal.strategies`, the underlying token of the strategy
     * NOTE: if `receiveAsTokens` is false, the `tokens` array is unused and can be filled with default values. However, `tokens.length` MUST still be equal to `withdrawal.strategies.length`.
     * NOTE: For the `beaconChainETHStrategy`, the corresponding `tokens` value is ignored (can be 0).
     * @param receiveAsTokens If true, withdrawn shares will be converted to tokens and sent to the caller. If false, the caller receives shares that can be delegated to an operator.
     * NOTE: if the caller receives shares and is currently delegated to an operator, the received shares are
     * automatically delegated to the caller's current operator.
     */
    function completeQueuedWithdrawal(
        Withdrawal calldata withdrawal,
        IERC20[] calldata tokens,
        bool receiveAsTokens
    ) external;

    /**
     * @notice Used to complete multiple queued withdrawals
     * @param withdrawals Array of Withdrawals to complete. See `completeQueuedWithdrawal` for the usage of a single Withdrawal.
     * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
     * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
     * @dev See `completeQueuedWithdrawal` for relevant dev tags
     */
    function completeQueuedWithdrawals(
        Withdrawal[] calldata withdrawals,
        IERC20[][] calldata tokens,
        bool[] calldata receiveAsTokens
    ) external;

    /**
     * @notice Called by a share manager when a staker's deposit share balance in a strategy increases.
     * This method delegates any new shares to an operator (if applicable), and updates the staker's
     * deposit scaling factor regardless.
     * @param staker The address whose deposit shares have increased
     * @param strategy The strategy in which shares have been deposited
     * @param prevDepositShares The number of deposit shares the staker had in the strategy prior to the increase
     * @param addedShares The number of deposit shares added by the staker
     *
     * @dev Note that if the either the staker's current operator has been slashed 100% for `strategy`, OR the
     * staker has been slashed 100% on the beacon chain such that the calculated slashing factor is 0, this
     * method WILL REVERT.
     */
    function increaseDelegatedShares(
        address staker,
        IStrategy strategy,
        uint256 prevDepositShares,
        uint256 addedShares
    ) external;

    /**
     * @notice If the staker is delegated, decreases its operator's shares in response to
     * a decrease in balance in the beaconChainETHStrategy
     * @param staker the staker whose operator's balance will be decreased
     * @param curDepositShares the current deposit shares held by the staker
     * @param beaconChainSlashingFactorDecrease the amount that the staker's beaconChainSlashingFactor has decreased by
     * @dev Note: `beaconChainSlashingFactorDecrease` are assumed to ALWAYS be < 1 WAD.
     * These invariants are maintained in the EigenPodManager.
     */
    function decreaseDelegatedShares(
        address staker,
        uint256 curDepositShares,
        uint64 beaconChainSlashingFactorDecrease
    ) external;

    /**
     * @notice Decreases the operator's shares in storage after a slash and increases the burnable shares by calling
     * into either the StrategyManager or EigenPodManager (if the strategy is beaconChainETH).
     * @param operator The operator to decrease shares for.
     * @param operatorSet The operator set to decrease shares for.
     * @param slashId The slash id to decrease shares for.
     * @param strategy The strategy to decrease shares for.
     * @param prevMaxMagnitude The previous maxMagnitude of the operator.
     * @param newMaxMagnitude The new maxMagnitude of the operator.
     * @dev Callable only by the AllocationManager.
     * @dev Note: Assumes `prevMaxMagnitude <= newMaxMagnitude`. This invariant is maintained in
     * the AllocationManager.
     * @return totalDepositSharesToSlash The total deposit shares to burn or redistribute.
     */
    function slashOperatorShares(
        address operator,
        OperatorSet calldata operatorSet,
        uint256 slashId,
        IStrategy strategy,
        uint64 prevMaxMagnitude,
        uint64 newMaxMagnitude
    ) external returns (uint256 totalDepositSharesToSlash);

    /**
     *
     *                         VIEW FUNCTIONS
     *
     */

    /**
     * @notice returns the address of the operator that `staker` is delegated to.
     * @notice Mapping: staker => operator whom the staker is currently delegated to.
     * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
     */
    function delegatedTo(
        address staker
    ) external view returns (address);

    /**
     * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
     * @dev Salts are used in the `delegateTo` function. Note that this function only processes the delegationApprover's
     * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
     */
    function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool);

    /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
    /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
    function cumulativeWithdrawalsQueued(
        address staker
    ) external view returns (uint256);

    /**
     * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
     */
    function isDelegated(
        address staker
    ) external view returns (bool);

    /**
     * @notice Returns true is an operator has previously registered for delegation.
     */
    function isOperator(
        address operator
    ) external view returns (bool);

    /**
     * @notice Returns the delegationApprover account for an operator
     */
    function delegationApprover(
        address operator
    ) external view returns (address);

    /**
     * @notice Returns the shares that an operator has delegated to them in a set of strategies
     * @param operator the operator to get shares for
     * @param strategies the strategies to get shares for
     */
    function getOperatorShares(
        address operator,
        IStrategy[] memory strategies
    ) external view returns (uint256[] memory);

    /**
     * @notice Returns the shares that a set of operators have delegated to them in a set of strategies
     * @param operators the operators to get shares for
     * @param strategies the strategies to get shares for
     */
    function getOperatorsShares(
        address[] memory operators,
        IStrategy[] memory strategies
    ) external view returns (uint256[][] memory);

    /**
     * @notice Returns amount of withdrawable shares from an operator for a strategy that is still in the queue
     * and therefore slashable. Note that the *actual* slashable amount could be less than this value as this doesn't account
     * for amounts that have already been slashed. This assumes that none of the shares have been slashed.
     * @param operator the operator to get shares for
     * @param strategy the strategy to get shares for
     * @return the amount of shares that are slashable in the withdrawal queue for an operator and a strategy
     */
    function getSlashableSharesInQueue(address operator, IStrategy strategy) external view returns (uint256);

    /**
     * @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal and the
     * corresponding depositShares.
     * This value depends on which operator the staker is delegated to.
     * The shares amount returned is the actual amount of Strategy shares the staker would receive (subject
     * to each strategy's underlying shares to token ratio).
     */
    function getWithdrawableShares(
        address staker,
        IStrategy[] memory strategies
    ) external view returns (uint256[] memory withdrawableShares, uint256[] memory depositShares);

    /**
     * @notice Returns the number of shares in storage for a staker and all their strategies
     */
    function getDepositedShares(
        address staker
    ) external view returns (IStrategy[] memory, uint256[] memory);

    /**
     * @notice Returns the scaling factor applied to a staker's deposits for a given strategy
     */
    function depositScalingFactor(address staker, IStrategy strategy) external view returns (uint256);

    /**
     * @notice Returns the Withdrawal associated with a `withdrawalRoot`.
     * @param withdrawalRoot The hash identifying the queued withdrawal.
     * @return withdrawal The withdrawal details.
     */
    function queuedWithdrawals(
        bytes32 withdrawalRoot
    ) external view returns (Withdrawal memory withdrawal);

    /**
     * @notice Returns the Withdrawal and corresponding shares associated with a `withdrawalRoot`
     * @param withdrawalRoot The hash identifying the queued withdrawal
     * @return withdrawal The withdrawal details
     * @return shares Array of shares corresponding to each strategy in the withdrawal
     * @dev The shares are what a user would receive from completing a queued withdrawal, assuming all slashings are applied
     * @dev Withdrawals queued before the slashing release cannot be queried with this method
     */
    function getQueuedWithdrawal(
        bytes32 withdrawalRoot
    ) external view returns (Withdrawal memory withdrawal, uint256[] memory shares);

    /**
     * @notice Returns all queued withdrawals and their corresponding shares for a staker.
     * @param staker The address of the staker to query withdrawals for.
     * @return withdrawals Array of Withdrawal structs containing details about each queued withdrawal.
     * @return shares 2D array of shares, where each inner array corresponds to the strategies in the withdrawal.
     * @dev The shares are what a user would receive from completing a queued withdrawal, assuming all slashings are applied.
     */
    function getQueuedWithdrawals(
        address staker
    ) external view returns (Withdrawal[] memory withdrawals, uint256[][] memory shares);

    /// @notice Returns a list of queued withdrawal roots for the `staker`.
    /// NOTE that this only returns withdrawals queued AFTER the slashing release.
    function getQueuedWithdrawalRoots(
        address staker
    ) external view returns (bytes32[] memory);

    /**
     * @notice Converts shares for a set of strategies to deposit shares, likely in order to input into `queueWithdrawals`.
     * This function will revert from a division by 0 error if any of the staker's strategies have a slashing factor of 0.
     * @param staker the staker to convert shares for
     * @param strategies the strategies to convert shares for
     * @param withdrawableShares the shares to convert
     * @return the deposit shares
     * @dev will be a few wei off due to rounding errors
     */
    function convertToDepositShares(
        address staker,
        IStrategy[] memory strategies,
        uint256[] memory withdrawableShares
    ) external view returns (uint256[] memory);

    /// @notice Returns the keccak256 hash of `withdrawal`.
    function calculateWithdrawalRoot(
        Withdrawal memory withdrawal
    ) external pure returns (bytes32);

    /**
     * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` function.
     * @param staker The account delegating their stake
     * @param operator The account receiving delegated stake
     * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
     * @param approverSalt A unique and single use value associated with the approver signature.
     * @param expiry Time after which the approver's signature becomes invalid
     */
    function calculateDelegationApprovalDigestHash(
        address staker,
        address operator,
        address _delegationApprover,
        bytes32 approverSalt,
        uint256 expiry
    ) external view returns (bytes32);

    /// @notice return address of the beaconChainETHStrategy
    function beaconChainETHStrategy() external view returns (IStrategy);

    /**
     * @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable.
     * Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject
     * to this longer delay.
     * @dev Backwards-compatible interface to return the internal `MIN_WITHDRAWAL_DELAY_BLOCKS` value
     * @dev Previous value in storage was deprecated. See `__deprecated_minWithdrawalDelayBlocks`
     */
    function minWithdrawalDelayBlocks() external view returns (uint32);

    /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
    function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "./IStrategy.sol";
import "./IShareManager.sol";
import "./IDelegationManager.sol";
import "./IEigenPodManager.sol";
import "./ISemVerMixin.sol";

interface IStrategyManagerErrors {
    /// @dev Thrown when total strategies deployed exceeds max.
    error MaxStrategiesExceeded();
    /// @dev Thrown when call attempted from address that's not delegation manager.
    error OnlyDelegationManager();
    /// @dev Thrown when call attempted from address that's not strategy whitelister.
    error OnlyStrategyWhitelister();
    /// @dev Thrown when provided `shares` amount is too high.
    error SharesAmountTooHigh();
    /// @dev Thrown when provided `shares` amount is zero.
    error SharesAmountZero();
    /// @dev Thrown when provided `staker` address is null.
    error StakerAddressZero();
    /// @dev Thrown when provided `strategy` not found.
    error StrategyNotFound();
    /// @dev Thrown when attempting to deposit to a non-whitelisted strategy.
    error StrategyNotWhitelisted();
    /// @dev Thrown when attempting to add a strategy that is already in the operator set's burn or redistributable shares.
    error StrategyAlreadyInSlash();
}

interface IStrategyManagerEvents {
    /**
     * @notice Emitted when a new deposit occurs on behalf of `staker`.
     * @param staker Is the staker who is depositing funds into EigenLayer.
     * @param strategy Is the strategy that `staker` has deposited into.
     * @param shares Is the number of new shares `staker` has been granted in `strategy`.
     */
    event Deposit(address staker, IStrategy strategy, uint256 shares);

    /// @notice Emitted when the `strategyWhitelister` is changed
    event StrategyWhitelisterChanged(address previousAddress, address newAddress);

    /// @notice Emitted when a strategy is added to the approved list of strategies for deposit
    event StrategyAddedToDepositWhitelist(IStrategy strategy);

    /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
    event StrategyRemovedFromDepositWhitelist(IStrategy strategy);

    /// @notice Emitted when an operator is slashed and shares to be burned or redistributed are increased
    event BurnOrRedistributableSharesIncreased(
        OperatorSet operatorSet, uint256 slashId, IStrategy strategy, uint256 shares
    );

    /// @notice Emitted when shares marked for burning or redistribution are decreased and transferred to the operator set's redistribution recipient
    event BurnOrRedistributableSharesDecreased(
        OperatorSet operatorSet, uint256 slashId, IStrategy strategy, uint256 shares
    );

    /// @notice Emitted when shares are burnt
    /// @dev This event is only emitted in the pre-redistribution slash path
    event BurnableSharesDecreased(IStrategy strategy, uint256 shares);
}

/**
 * @title Interface for the primary entrypoint for funds into EigenLayer.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice See the `StrategyManager` contract itself for implementation details.
 */
interface IStrategyManager is IStrategyManagerErrors, IStrategyManagerEvents, IShareManager, ISemVerMixin {
    /**
     * @notice Initializes the strategy manager contract. Sets the `pauserRegistry` (currently **not** modifiable after being set),
     * and transfers contract ownership to the specified `initialOwner`.
     * @param initialOwner Ownership of this contract is transferred to this address.
     * @param initialStrategyWhitelister The initial value of `strategyWhitelister` to set.
     * @param initialPausedStatus The initial value of `_paused` to set.
     */
    function initialize(
        address initialOwner,
        address initialStrategyWhitelister,
        uint256 initialPausedStatus
    ) external;

    /**
     * @notice Deposits `amount` of `token` into the specified `strategy` and credits shares to the caller
     * @param strategy the strategy that handles `token`
     * @param token the token from which the `amount` will be transferred
     * @param amount the number of tokens to deposit
     * @return depositShares the number of deposit shares credited to the caller
     * @dev The caller must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
     *
     * WARNING: Be extremely cautious when depositing tokens that do not strictly adhere to ERC20 standards.
     * Tokens that diverge significantly from ERC20 norms can cause unexpected behavior in token balances for
     * that strategy, e.g. ERC-777 tokens allowing cross-contract reentrancy.
     */
    function depositIntoStrategy(
        IStrategy strategy,
        IERC20 token,
        uint256 amount
    ) external returns (uint256 depositShares);

    /**
     * @notice Deposits `amount` of `token` into the specified `strategy` and credits shares to the `staker`
     * Note tokens are transferred from `msg.sender`, NOT from `staker`. This method allows the caller, using a
     * signature, to deposit their tokens to another staker's balance.
     * @param strategy the strategy that handles `token`
     * @param token the token from which the `amount` will be transferred
     * @param amount the number of tokens to transfer from the caller to the strategy
     * @param staker the staker that the deposited assets will be credited to
     * @param expiry the timestamp at which the signature expires
     * @param signature a valid ECDSA or EIP-1271 signature from `staker`
     * @return depositShares the number of deposit shares credited to `staker`
     * @dev The caller must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
     *
     * WARNING: Be extremely cautious when depositing tokens that do not strictly adhere to ERC20 standards.
     * Tokens that diverge significantly from ERC20 norms can cause unexpected behavior in token balances for
     * that strategy, e.g. ERC-777 tokens allowing cross-contract reentrancy.
     */
    function depositIntoStrategyWithSignature(
        IStrategy strategy,
        IERC20 token,
        uint256 amount,
        address staker,
        uint256 expiry,
        bytes memory signature
    ) external returns (uint256 depositShares);

    /**
     * @notice Legacy burn strategy shares for the given strategy by calling into the strategy to transfer
     * to the default burn address.
     * @param strategy The strategy to burn shares in.
     * @dev This function will be DEPRECATED in a release after redistribution
     */
    function burnShares(
        IStrategy strategy
    ) external;

    /**
     * @notice Removes burned shares from storage and transfers the underlying tokens for the slashId to the redistribution recipient.
     * @dev Reentrancy is checked in the `clearBurnOrRedistributableSharesByStrategy` function.
     * @param operatorSet The operator set to burn shares in.
     * @param slashId The slash ID to burn shares in.
     * @return The amounts of tokens transferred to the redistribution recipient for each strategy
     */
    function clearBurnOrRedistributableShares(
        OperatorSet calldata operatorSet,
        uint256 slashId
    ) external returns (uint256[] memory);

    /**
     * @notice Removes a single strategy's shares from storage and transfers the underlying tokens for the slashId to the redistribution recipient.
     * @param operatorSet The operator set to burn shares in.
     * @param slashId The slash ID to burn shares in.
     * @param strategy The strategy to burn shares in.
     * @return The amount of tokens transferred to the redistribution recipient for the strategy.
     */
    function clearBurnOrRedistributableSharesByStrategy(
        OperatorSet calldata operatorSet,
        uint256 slashId,
        IStrategy strategy
    ) external returns (uint256);

    /**
     * @notice Returns the strategies and shares that have NOT been sent to the redistribution recipient for a given slashId.
     * @param operatorSet The operator set to burn or redistribute shares in.
     * @param slashId The slash ID to burn or redistribute shares in.
     * @return The strategies and shares for the given slashId.
     */
    function getBurnOrRedistributableShares(
        OperatorSet calldata operatorSet,
        uint256 slashId
    ) external view returns (IStrategy[] memory, uint256[] memory);

    /**
     * @notice Returns the shares for a given strategy for a given slashId.
     * @param operatorSet The operator set to burn or redistribute shares in.
     * @param slashId The slash ID to burn or redistribute shares in.
     * @param strategy The strategy to get the shares for.
     * @return The shares for the given strategy for the given slashId.
     * @dev This function will return revert if the shares have already been sent to the redistribution recipient.
     */
    function getBurnOrRedistributableShares(
        OperatorSet calldata operatorSet,
        uint256 slashId,
        IStrategy strategy
    ) external view returns (uint256);

    /**
     * @notice Returns the number of strategies that have NOT been sent to the redistribution recipient for a given slashId.
     * @param operatorSet The operator set to burn or redistribute shares in.
     * @param slashId The slash ID to burn or redistribute shares in.
     * @return The number of strategies for the given slashId.
     */
    function getBurnOrRedistributableCount(
        OperatorSet calldata operatorSet,
        uint256 slashId
    ) external view returns (uint256);

    /**
     * @notice Owner-only function to change the `strategyWhitelister` address.
     * @param newStrategyWhitelister new address for the `strategyWhitelister`.
     */
    function setStrategyWhitelister(
        address newStrategyWhitelister
    ) external;

    /**
     * @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into
     * @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already)
     */
    function addStrategiesToDepositWhitelist(
        IStrategy[] calldata strategiesToWhitelist
    ) external;

    /**
     * @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into
     * @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it)
     */
    function removeStrategiesFromDepositWhitelist(
        IStrategy[] calldata strategiesToRemoveFromWhitelist
    ) external;

    /// @notice Returns bool for whether or not `strategy` is whitelisted for deposit
    function strategyIsWhitelistedForDeposit(
        IStrategy strategy
    ) external view returns (bool);

    /**
     * @notice Get all details on the staker's deposits and corresponding shares
     * @return (staker's strategies, shares in these strategies)
     */
    function getDeposits(
        address staker
    ) external view returns (IStrategy[] memory, uint256[] memory);

    function getStakerStrategyList(
        address staker
    ) external view returns (IStrategy[] memory);

    /// @notice Simple getter function that returns `stakerStrategyList[staker].length`.
    function stakerStrategyListLength(
        address staker
    ) external view returns (uint256);

    /// @notice Returns the current shares of `user` in `strategy`
    function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 shares);

    /// @notice Returns the single, central Delegation contract of EigenLayer
    function delegation() external view returns (IDelegationManager);

    /// @notice Returns the address of the `strategyWhitelister`
    function strategyWhitelister() external view returns (address);

    /// @notice Returns the burnable shares of a strategy
    /// @dev This function will be deprecated in a release after redistribution
    function getBurnableShares(
        IStrategy strategy
    ) external view returns (uint256);

    /**
     * @notice Gets every strategy with burnable shares and the amount of burnable shares in each said strategy
     *
     * @dev This function will be deprecated in a release after redistribution
     * WARNING: This operation can copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Users should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function getStrategiesWithBurnableShares() external view returns (address[] memory, uint256[] memory);

    /**
     * @param staker The address of the staker.
     * @param strategy The strategy to deposit into.
     * @param token The token to deposit.
     * @param amount The amount of `token` to deposit.
     * @param nonce The nonce of the staker.
     * @param expiry The expiry of the signature.
     * @return The EIP-712 signable digest hash.
     */
    function calculateStrategyDepositDigestHash(
        address staker,
        IStrategy strategy,
        IERC20 token,
        uint256 amount,
        uint256 nonce,
        uint256 expiry
    ) external view returns (bytes32);

    /**
     * @notice Returns the operator sets that have pending burn or redistributable shares.
     * @return The operator sets that have pending burn or redistributable shares.
     */
    function getPendingOperatorSets() external view returns (OperatorSet[] memory);

    /**
     * @notice Returns the slash IDs that are pending to be burned or redistributed.
     * @dev This function will return revert if the operator set has no pending burn or redistributable shares.
     * @param operatorSet The operator set to get the pending slash IDs for.
     * @return The slash IDs that are pending to be burned or redistributed.
     */
    function getPendingSlashIds(
        OperatorSet calldata operatorSet
    ) external view returns (uint256[] memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

using OperatorSetLib for OperatorSet global;

/**
 * @notice An operator set identified by the AVS address and an identifier
 * @param avs The address of the AVS this operator set belongs to
 * @param id The unique identifier for the operator set
 */
struct OperatorSet {
    address avs;
    uint32 id;
}

library OperatorSetLib {
    function key(
        OperatorSet memory os
    ) internal pure returns (bytes32) {
        return bytes32(abi.encodePacked(os.avs, uint96(os.id)));
    }

    function decode(
        bytes32 _key
    ) internal pure returns (OperatorSet memory) {
        /// forgefmt: disable-next-item
        return OperatorSet({
            avs: address(uint160(uint256(_key) >> 96)),
            id: uint32(uint256(_key) & type(uint96).max)
        });
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/SlashingLib.sol";
import "./ISemVerMixin.sol";

interface IStrategyErrors {
    /// @dev Thrown when called by an account that is not strategy manager.
    error OnlyStrategyManager();
    /// @dev Thrown when new shares value is zero.
    error NewSharesZero();
    /// @dev Thrown when total shares exceeds max.
    error TotalSharesExceedsMax();
    /// @dev Thrown when amount shares is greater than total shares.
    error WithdrawalAmountExceedsTotalDeposits();
    /// @dev Thrown when attempting an action with a token that is not accepted.
    error OnlyUnderlyingToken();

    /// StrategyBaseWithTVLLimits

    /// @dev Thrown when `maxPerDeposit` exceeds max.
    error MaxPerDepositExceedsMax();
    /// @dev Thrown when balance exceeds max total deposits.
    error BalanceExceedsMaxTotalDeposits();
}

interface IStrategyEvents {
    /**
     * @notice Used to emit an event for the exchange rate between 1 share and underlying token in a strategy contract
     * @param rate is the exchange rate in wad 18 decimals
     * @dev Tokens that do not have 18 decimals must have offchain services scale the exchange rate by the proper magnitude
     */
    event ExchangeRateEmitted(uint256 rate);

    /**
     * Used to emit the underlying token and its decimals on strategy creation
     * @notice token
     * @param token is the ERC20 token of the strategy
     * @param decimals are the decimals of the ERC20 token in the strategy
     */
    event StrategyTokenSet(IERC20 token, uint8 decimals);
}

/**
 * @title Minimal interface for an `Strategy` contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice Custom `Strategy` implementations may expand extensively on this interface.
 */
interface IStrategy is IStrategyErrors, IStrategyEvents, ISemVerMixin {
    /**
     * @notice Used to deposit tokens into this Strategy
     * @param token is the ERC20 token being deposited
     * @param amount is the amount of token being deposited
     * @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
     * `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
     * @return newShares is the number of new shares issued at the current exchange ratio.
     */
    function deposit(IERC20 token, uint256 amount) external returns (uint256);

    /**
     * @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address
     * @param recipient is the address to receive the withdrawn funds
     * @param token is the ERC20 token being transferred out
     * @param amountShares is the amount of shares being withdrawn
     * @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
     * other functions, and individual share balances are recorded in the strategyManager as well.
     * @return amountOut is the amount of tokens being transferred out.
     */
    function withdraw(address recipient, IERC20 token, uint256 amountShares) external returns (uint256);

    /**
     * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
     * For a staker using this function and trying to calculate the amount of underlying tokens they have in total they
     * should input into `amountShares` their withdrawable shares read from the `DelegationManager` contract.
     * @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
     * @param amountShares is the amount of shares to calculate its conversion into the underlying token
     * @return The amount of underlying tokens corresponding to the input `amountShares`
     * @dev Implementation for these functions in particular may vary significantly for different strategies
     */
    function sharesToUnderlying(
        uint256 amountShares
    ) external returns (uint256);

    /**
     * @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
     * @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
     * @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
     * @return The amount of shares corresponding to the input `amountUnderlying`.  This is used as deposit shares
     * in the `StrategyManager` contract.
     * @dev Implementation for these functions in particular may vary significantly for different strategies
     */
    function underlyingToShares(
        uint256 amountUnderlying
    ) external returns (uint256);

    /**
     * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
     * this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
     */
    function userUnderlying(
        address user
    ) external returns (uint256);

    /**
     * @notice convenience function for fetching the current total shares of `user` in this strategy, by
     * querying the `strategyManager` contract
     */
    function shares(
        address user
    ) external view returns (uint256);

    /**
     * @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
     * For a staker using this function and trying to calculate the amount of underlying tokens they have in total they
     * should input into `amountShares` their withdrawable shares read from the `DelegationManager` contract.
     * @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
     * @param amountShares is the amount of shares to calculate its conversion into the underlying token
     * @return The amount of underlying tokens corresponding to the input `amountShares`
     * @dev Implementation for these functions in particular may vary significantly for different strategies
     */
    function sharesToUnderlyingView(
        uint256 amountShares
    ) external view returns (uint256);

    /**
     * @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
     * @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
     * @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
     * @return The amount of shares corresponding to the input `amountUnderlying`. This is used as deposit shares
     * in the `StrategyManager` contract.
     * @dev Implementation for these functions in particular may vary significantly for different strategies
     */
    function underlyingToSharesView(
        uint256 amountUnderlying
    ) external view returns (uint256);

    /**
     * @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
     * this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
     */
    function userUnderlyingView(
        address user
    ) external view returns (uint256);

    /// @notice The underlying token for shares in this Strategy
    function underlyingToken() external view returns (IERC20);

    /// @notice The total number of extant shares in this Strategy
    function totalShares() external view returns (uint256);

    /// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
    function explanation() external view returns (string memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IStrategy.sol";
import "./ISemVerMixin.sol";

/**
 * @title Interface for the `StrategyFactory` contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @dev This may not be compatible with non-standard ERC20 tokens. Caution is warranted.
 */
interface IStrategyFactory is ISemVerMixin {
    /// @dev Thrown when attempting to deploy a strategy for a blacklisted token.
    error BlacklistedToken();
    /// @dev Thrown when attempting to deploy a strategy that already exists.
    error StrategyAlreadyExists();
    /// @dev Thrown when attempting to blacklist a token that is already blacklisted
    error AlreadyBlacklisted();

    event TokenBlacklisted(IERC20 token);

    /// @notice Upgradeable beacon which new Strategies deployed by this contract point to
    function strategyBeacon() external view returns (IBeacon);

    /// @notice Mapping token => Strategy contract for the token
    /// The strategies in this mapping are deployed by the StrategyFactory.
    /// The factory can only deploy a single strategy per token address
    /// These strategies MIGHT not be whitelisted in the StrategyManager,
    /// though deployNewStrategy does whitelist by default.
    /// These strategies MIGHT not be the only strategy for the underlying token
    /// as additional strategies can be whitelisted by the owner of the factory.
    function deployedStrategies(
        IERC20 token
    ) external view returns (IStrategy);

    /**
     * @notice Deploy a new strategyBeacon contract for the ERC20 token.
     * @param token the token to deploy a strategy for
     * @dev A strategy contract must not yet exist for the token.
     * $dev Immense caution is warranted for non-standard ERC20 tokens, particularly "reentrant" tokens
     * like those that conform to ERC777.
     */
    function deployNewStrategy(
        IERC20 token
    ) external returns (IStrategy newStrategy);

    /**
     * @notice Owner-only function to pass through a call to `StrategyManager.addStrategiesToDepositWhitelist`
     */
    function whitelistStrategies(
        IStrategy[] calldata strategiesToWhitelist
    ) external;

    /**
     * @notice Owner-only function to pass through a call to `StrategyManager.removeStrategiesFromDepositWhitelist`
     */
    function removeStrategiesFromWhitelist(
        IStrategy[] calldata strategiesToRemoveFromWhitelist
    ) external;

    /// @notice Emitted when the `strategyBeacon` is changed
    event StrategyBeaconModified(IBeacon previousBeacon, IBeacon newBeacon);

    /// @notice Emitted whenever a slot is set in the `tokenStrategy` mapping
    event StrategySetForToken(IERC20 token, IStrategy strategy);
}

File 9 of 81 : IRewardsCoordinator.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/OperatorSetLib.sol";

import "./IAllocationManager.sol";
import "./IDelegationManager.sol";
import "./IStrategyManager.sol";
import "./IPauserRegistry.sol";
import "./IPermissionController.sol";
import "./IStrategy.sol";
import "./ISemVerMixin.sol";

interface IRewardsCoordinatorErrors {
    /// @dev Thrown when msg.sender is not allowed to call a function
    error UnauthorizedCaller();
    /// @dev Thrown when a earner not an AVS or Operator
    error InvalidEarner();

    /// Invalid Inputs

    /// @dev Thrown when an input address is zero
    error InvalidAddressZero();
    /// @dev Thrown when an invalid root is provided.
    error InvalidRoot();
    /// @dev Thrown when an invalid root index is provided.
    error InvalidRootIndex();
    /// @dev Thrown when input arrays length is zero.
    error InputArrayLengthZero();
    /// @dev Thrown when two array parameters have mismatching lengths.
    error InputArrayLengthMismatch();
    /// @dev Thrown when provided root is not for new calculated period.
    error NewRootMustBeForNewCalculatedPeriod();
    /// @dev Thrown when rewards end timestamp has not elapsed.
    error RewardsEndTimestampNotElapsed();
    /// @dev Thrown when an invalid operator set is provided.
    error InvalidOperatorSet();

    /// Rewards Submissions

    /// @dev Thrown when input `amount` is zero.
    error AmountIsZero();
    /// @dev Thrown when input `amount` exceeds maximum.
    error AmountExceedsMax();
    /// @dev Thrown when input `split` exceeds `ONE_HUNDRED_IN_BIPS`
    error SplitExceedsMax();
    /// @dev Thrown when an operator attempts to set a split before the previous one becomes active
    error PreviousSplitPending();
    /// @dev Thrown when input `duration` exceeds maximum.
    error DurationExceedsMax();
    /// @dev Thrown when input `duration` is zero.
    error DurationIsZero();
    /// @dev Thrown when input `duration` is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
    error InvalidDurationRemainder();
    /// @dev Thrown when GENESIS_REWARDS_TIMESTAMP is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
    error InvalidGenesisRewardsTimestampRemainder();
    /// @dev Thrown when CALCULATION_INTERVAL_SECONDS is not evenly divisble by SNAPSHOT_CADENCE.
    error InvalidCalculationIntervalSecondsRemainder();
    /// @dev Thrown when `startTimestamp` is not evenly divisble by CALCULATION_INTERVAL_SECONDS.
    error InvalidStartTimestampRemainder();
    /// @dev Thrown when `startTimestamp` is too far in the future.
    error StartTimestampTooFarInFuture();
    /// @dev Thrown when `startTimestamp` is too far in the past.
    error StartTimestampTooFarInPast();
    /// @dev Thrown when an attempt to use a non-whitelisted strategy is made.
    error StrategyNotWhitelisted();
    /// @dev Thrown when `strategies` is not sorted in ascending order.
    error StrategiesNotInAscendingOrder();
    /// @dev Thrown when `operators` are not sorted in ascending order
    error OperatorsNotInAscendingOrder();
    /// @dev Thrown when an operator-directed rewards submission is not retroactive
    error SubmissionNotRetroactive();

    /// Claims

    /// @dev Thrown when an invalid earner claim proof is provided.
    error InvalidClaimProof();
    /// @dev Thrown when an invalid token leaf index is provided.
    error InvalidTokenLeafIndex();
    /// @dev Thrown when an invalid earner leaf index is provided.
    error InvalidEarnerLeafIndex();
    /// @dev Thrown when cumulative earnings are not greater than cumulative claimed.
    error EarningsNotGreaterThanClaimed();

    /// Reward Root Checks

    /// @dev Thrown if a root has already been disabled.
    error RootDisabled();
    /// @dev Thrown if a root has not been activated yet.
    error RootNotActivated();
    /// @dev Thrown if a root has already been activated.
    error RootAlreadyActivated();
}

interface IRewardsCoordinatorTypes {
    /**
     * @notice A linear combination of strategies and multipliers for AVSs to weigh
     * EigenLayer strategies.
     * @param strategy The EigenLayer strategy to be used for the rewards submission
     * @param multiplier The weight of the strategy in the rewards submission
     */
    struct StrategyAndMultiplier {
        IStrategy strategy;
        uint96 multiplier;
    }

    /**
     * @notice A reward struct for an operator
     * @param operator The operator to be rewarded
     * @param amount The reward amount for the operator
     */
    struct OperatorReward {
        address operator;
        uint256 amount;
    }

    /**
     * @notice A split struct for an Operator
     * @param oldSplitBips The old split in basis points. This is the split that is active if `block.timestamp < activatedAt`
     * @param newSplitBips The new split in basis points. This is the split that is active if `block.timestamp >= activatedAt`
     * @param activatedAt The timestamp at which the split will be activated
     */
    struct OperatorSplit {
        uint16 oldSplitBips;
        uint16 newSplitBips;
        uint32 activatedAt;
    }

    /**
     * Sliding Window for valid RewardsSubmission startTimestamp
     *
     * Scenario A: GENESIS_REWARDS_TIMESTAMP IS WITHIN RANGE
     *         <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
     *             <--------------------valid range for startTimestamp------------------------>
     *             ^
     *         GENESIS_REWARDS_TIMESTAMP
     *
     *
     * Scenario B: GENESIS_REWARDS_TIMESTAMP IS OUT OF RANGE
     *         <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH--->
     *         <------------------------valid range for startTimestamp------------------------>
     *     ^
     * GENESIS_REWARDS_TIMESTAMP
     * @notice RewardsSubmission struct submitted by AVSs when making rewards for their operators and stakers
     * RewardsSubmission can be for a time range within the valid window for startTimestamp and must be within max duration.
     * See `createAVSRewardsSubmission()` for more details.
     * @param strategiesAndMultipliers The strategies and their relative weights
     * cannot have duplicate strategies and need to be sorted in ascending address order
     * @param token The rewards token to be distributed
     * @param amount The total amount of tokens to be distributed
     * @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution
     * could start in the past or in the future but within a valid range. See the diagram above.
     * @param duration The duration of the submission range in seconds. Must be <= MAX_REWARDS_DURATION
     */
    struct RewardsSubmission {
        StrategyAndMultiplier[] strategiesAndMultipliers;
        IERC20 token;
        uint256 amount;
        uint32 startTimestamp;
        uint32 duration;
    }

    /**
     * @notice OperatorDirectedRewardsSubmission struct submitted by AVSs when making operator-directed rewards for their operators and stakers.
     * @param strategiesAndMultipliers The strategies and their relative weights.
     * @param token The rewards token to be distributed.
     * @param operatorRewards The rewards for the operators.
     * @param startTimestamp The timestamp (seconds) at which the submission range is considered for distribution.
     * @param duration The duration of the submission range in seconds.
     * @param description Describes what the rewards submission is for.
     */
    struct OperatorDirectedRewardsSubmission {
        StrategyAndMultiplier[] strategiesAndMultipliers;
        IERC20 token;
        OperatorReward[] operatorRewards;
        uint32 startTimestamp;
        uint32 duration;
        string description;
    }

    /**
     * @notice A distribution root is a merkle root of the distribution of earnings for a given period.
     * The RewardsCoordinator stores all historical distribution roots so that earners can claim their earnings against older roots
     * if they wish but the merkle tree contains the cumulative earnings of all earners and tokens for a given period so earners (or their claimers if set)
     * only need to claim against the latest root to claim all available earnings.
     * @param root The merkle root of the distribution
     * @param rewardsCalculationEndTimestamp The timestamp (seconds) until which rewards have been calculated
     * @param activatedAt The timestamp (seconds) at which the root can be claimed against
     */
    struct DistributionRoot {
        bytes32 root;
        uint32 rewardsCalculationEndTimestamp;
        uint32 activatedAt;
        bool disabled;
    }

    /**
     * @notice Internal leaf in the merkle tree for the earner's account leaf
     * @param earner The address of the earner
     * @param earnerTokenRoot The merkle root of the earner's token subtree
     * Each leaf in the earner's token subtree is a TokenTreeMerkleLeaf
     */
    struct EarnerTreeMerkleLeaf {
        address earner;
        bytes32 earnerTokenRoot;
    }

    /**
     * @notice The actual leaves in the distribution merkle tree specifying the token earnings
     * for the respective earner's subtree. Each leaf is a claimable amount of a token for an earner.
     * @param token The token for which the earnings are being claimed
     * @param cumulativeEarnings The cumulative earnings of the earner for the token
     */
    struct TokenTreeMerkleLeaf {
        IERC20 token;
        uint256 cumulativeEarnings;
    }

    /**
     * @notice A claim against a distribution root called by an
     * earners claimer (could be the earner themselves). Each token claim will claim the difference
     * between the cumulativeEarnings of the earner and the cumulativeClaimed of the claimer.
     * Each claim can specify which of the earner's earned tokens they want to claim.
     * See `processClaim()` for more details.
     * @param rootIndex The index of the root in the list of DistributionRoots
     * @param earnerIndex The index of the earner's account root in the merkle tree
     * @param earnerTreeProof The proof of the earner's EarnerTreeMerkleLeaf against the merkle root
     * @param earnerLeaf The earner's EarnerTreeMerkleLeaf struct, providing the earner address and earnerTokenRoot
     * @param tokenIndices The indices of the token leaves in the earner's subtree
     * @param tokenTreeProofs The proofs of the token leaves against the earner's earnerTokenRoot
     * @param tokenLeaves The token leaves to be claimed
     * @dev The merkle tree is structured with the merkle root at the top and EarnerTreeMerkleLeaf as internal leaves
     * in the tree. Each earner leaf has its own subtree with TokenTreeMerkleLeaf as leaves in the subtree.
     * To prove a claim against a specified rootIndex(which specifies the distributionRoot being used),
     * the claim will first verify inclusion of the earner leaf in the tree against _distributionRoots[rootIndex].root.
     * Then for each token, it will verify inclusion of the token leaf in the earner's subtree against the earner's earnerTokenRoot.
     */
    struct RewardsMerkleClaim {
        uint32 rootIndex;
        uint32 earnerIndex;
        bytes earnerTreeProof;
        EarnerTreeMerkleLeaf earnerLeaf;
        uint32[] tokenIndices;
        bytes[] tokenTreeProofs;
        TokenTreeMerkleLeaf[] tokenLeaves;
    }

    /**
     * @notice Parameters for the RewardsCoordinator constructor
     * @param delegationManager The address of the DelegationManager contract
     * @param strategyManager The address of the StrategyManager contract
     * @param allocationManager The address of the AllocationManager contract
     * @param pauserRegistry The address of the PauserRegistry contract
     * @param permissionController The address of the PermissionController contract
     * @param CALCULATION_INTERVAL_SECONDS The interval at which rewards are calculated
     * @param MAX_REWARDS_DURATION The maximum duration of a rewards submission
     * @param MAX_RETROACTIVE_LENGTH The maximum retroactive length of a rewards submission
     * @param MAX_FUTURE_LENGTH The maximum future length of a rewards submission
     * @param GENESIS_REWARDS_TIMESTAMP The timestamp at which rewards are first calculated
     * @param version The semantic version of the contract (e.g. "1.2.3")
     * @dev Needed to avoid stack-too-deep errors
     */
    struct RewardsCoordinatorConstructorParams {
        IDelegationManager delegationManager;
        IStrategyManager strategyManager;
        IAllocationManager allocationManager;
        IPauserRegistry pauserRegistry;
        IPermissionController permissionController;
        uint32 CALCULATION_INTERVAL_SECONDS;
        uint32 MAX_REWARDS_DURATION;
        uint32 MAX_RETROACTIVE_LENGTH;
        uint32 MAX_FUTURE_LENGTH;
        uint32 GENESIS_REWARDS_TIMESTAMP;
        string version;
    }
}

interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
    /// @notice emitted when an AVS creates a valid RewardsSubmission
    event AVSRewardsSubmissionCreated(
        address indexed avs,
        uint256 indexed submissionNonce,
        bytes32 indexed rewardsSubmissionHash,
        RewardsSubmission rewardsSubmission
    );

    /// @notice emitted when a valid RewardsSubmission is created for all stakers by a valid submitter
    event RewardsSubmissionForAllCreated(
        address indexed submitter,
        uint256 indexed submissionNonce,
        bytes32 indexed rewardsSubmissionHash,
        RewardsSubmission rewardsSubmission
    );

    /// @notice emitted when a valid RewardsSubmission is created when rewardAllStakersAndOperators is called
    event RewardsSubmissionForAllEarnersCreated(
        address indexed tokenHopper,
        uint256 indexed submissionNonce,
        bytes32 indexed rewardsSubmissionHash,
        RewardsSubmission rewardsSubmission
    );

    /**
     * @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission`
     * @param caller The address calling `createOperatorDirectedAVSRewardsSubmission`.
     * @param avs The avs on behalf of which the operator-directed rewards are being submitted.
     * @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`).
     * @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
     * @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers.
     */
    event OperatorDirectedAVSRewardsSubmissionCreated(
        address indexed caller,
        address indexed avs,
        bytes32 indexed operatorDirectedRewardsSubmissionHash,
        uint256 submissionNonce,
        OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
    );

    /**
     * @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission` for an operator set.
     * @param caller The address calling `createOperatorDirectedOperatorSetRewardsSubmission`.
     * @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`).
     * @param operatorSet The operatorSet on behalf of which the operator-directed rewards are being submitted.
     * @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
     * @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers.
     */
    event OperatorDirectedOperatorSetRewardsSubmissionCreated(
        address indexed caller,
        bytes32 indexed operatorDirectedRewardsSubmissionHash,
        OperatorSet operatorSet,
        uint256 submissionNonce,
        OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
    );

    /// @notice rewardsUpdater is responsible for submitting DistributionRoots, only owner can set rewardsUpdater
    event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater);

    event RewardsForAllSubmitterSet(
        address indexed rewardsForAllSubmitter, bool indexed oldValue, bool indexed newValue
    );

    event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay);
    event DefaultOperatorSplitBipsSet(uint16 oldDefaultOperatorSplitBips, uint16 newDefaultOperatorSplitBips);

    /**
     * @notice Emitted when the operator split for an AVS is set.
     * @param caller The address calling `setOperatorAVSSplit`.
     * @param operator The operator on behalf of which the split is being set.
     * @param avs The avs for which the split is being set by the operator.
     * @param activatedAt The timestamp at which the split will be activated.
     * @param oldOperatorAVSSplitBips The old split for the operator for the AVS.
     * @param newOperatorAVSSplitBips The new split for the operator for the AVS.
     */
    event OperatorAVSSplitBipsSet(
        address indexed caller,
        address indexed operator,
        address indexed avs,
        uint32 activatedAt,
        uint16 oldOperatorAVSSplitBips,
        uint16 newOperatorAVSSplitBips
    );

    /**
     * @notice Emitted when the operator split for Programmatic Incentives is set.
     * @param caller The address calling `setOperatorPISplit`.
     * @param operator The operator on behalf of which the split is being set.
     * @param activatedAt The timestamp at which the split will be activated.
     * @param oldOperatorPISplitBips The old split for the operator for Programmatic Incentives.
     * @param newOperatorPISplitBips The new split for the operator for Programmatic Incentives.
     */
    event OperatorPISplitBipsSet(
        address indexed caller,
        address indexed operator,
        uint32 activatedAt,
        uint16 oldOperatorPISplitBips,
        uint16 newOperatorPISplitBips
    );

    /**
     * @notice Emitted when the operator split for a given operatorSet is set.
     * @param caller The address calling `setOperatorSetSplit`.
     * @param operator The operator on behalf of which the split is being set.
     * @param operatorSet The operatorSet for which the split is being set.
     * @param activatedAt The timestamp at which the split will be activated.
     * @param oldOperatorSetSplitBips The old split for the operator for the operatorSet.
     * @param newOperatorSetSplitBips The new split for the operator for the operatorSet.
     */
    event OperatorSetSplitBipsSet(
        address indexed caller,
        address indexed operator,
        OperatorSet operatorSet,
        uint32 activatedAt,
        uint16 oldOperatorSetSplitBips,
        uint16 newOperatorSetSplitBips
    );

    event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer);

    /// @notice rootIndex is the specific array index of the newly created root in the storage array
    event DistributionRootSubmitted(
        uint32 indexed rootIndex,
        bytes32 indexed root,
        uint32 indexed rewardsCalculationEndTimestamp,
        uint32 activatedAt
    );

    event DistributionRootDisabled(uint32 indexed rootIndex);

    /// @notice root is one of the submitted distribution roots that was claimed against
    event RewardsClaimed(
        bytes32 root,
        address indexed earner,
        address indexed claimer,
        address indexed recipient,
        IERC20 token,
        uint256 claimedAmount
    );
}

/**
 * @title Interface for the `IRewardsCoordinator` contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice Allows AVSs to make "Rewards Submissions", which get distributed amongst the AVSs' confirmed
 * Operators and the Stakers delegated to those Operators.
 * Calculations are performed based on the completed RewardsSubmission, with the results posted in
 * a Merkle root against which Stakers & Operators can make claims.
 */
interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorEvents, ISemVerMixin {
    /**
     * @dev Initializes the addresses of the initial owner, pauser registry, rewardsUpdater and
     * configures the initial paused status, activationDelay, and defaultOperatorSplitBips.
     */
    function initialize(
        address initialOwner,
        uint256 initialPausedStatus,
        address _rewardsUpdater,
        uint32 _activationDelay,
        uint16 _defaultSplitBips
    ) external;

    /**
     * @notice Creates a new rewards submission on behalf of an AVS, to be split amongst the
     * set of stakers delegated to operators who are registered to the `avs`
     * @param rewardsSubmissions The rewards submissions being created
     * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made
     * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
     * @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
     * @dev The tokens are sent to the `RewardsCoordinator` contract
     * @dev Strategies must be in ascending order of addresses to check for duplicates
     * @dev This function will revert if the `rewardsSubmission` is malformed,
     * e.g. if the `strategies` and `weights` arrays are of non-equal lengths
     */
    function createAVSRewardsSubmission(
        RewardsSubmission[] calldata rewardsSubmissions
    ) external;

    /**
     * @notice similar to `createAVSRewardsSubmission` except the rewards are split amongst *all* stakers
     * rather than just those delegated to operators who are registered to a single avs and is
     * a permissioned call based on isRewardsForAllSubmitter mapping.
     * @param rewardsSubmissions The rewards submissions being created
     */
    function createRewardsForAllSubmission(
        RewardsSubmission[] calldata rewardsSubmissions
    ) external;

    /**
     * @notice Creates a new rewards submission for all earners across all AVSs.
     * Earners in this case indicating all operators and their delegated stakers. Undelegated stake
     * is not rewarded from this RewardsSubmission. This interface is only callable
     * by the token hopper contract from the Eigen Foundation
     * @param rewardsSubmissions The rewards submissions being created
     */
    function createRewardsForAllEarners(
        RewardsSubmission[] calldata rewardsSubmissions
    ) external;

    /**
     * @notice Creates a new operator-directed rewards submission on behalf of an AVS, to be split amongst the operators and
     * set of stakers delegated to operators who are registered to the `avs`.
     * @param avs The AVS on behalf of which the reward is being submitted
     * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created
     * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the submission is being made
     * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
     * @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
     * @dev The tokens are sent to the `RewardsCoordinator` contract
     * @dev The `RewardsCoordinator` contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function.
     * @dev Strategies must be in ascending order of addresses to check for duplicates
     * @dev Operators must be in ascending order of addresses to check for duplicates.
     * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed.
     */
    function createOperatorDirectedAVSRewardsSubmission(
        address avs,
        OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
    ) external;

    /**
     * @notice Creates a new operator-directed rewards submission for an operator set, to be split amongst the operators and
     * set of stakers delegated to operators who are part of the operator set.
     * @param operatorSet The operator set for which the rewards are being submitted
     * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created
     * @dev Expected to be called by the AVS that created the operator set
     * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
     * @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
     * @dev The tokens are sent to the `RewardsCoordinator` contract
     * @dev The `RewardsCoordinator` contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function
     * @dev Strategies must be in ascending order of addresses to check for duplicates
     * @dev Operators must be in ascending order of addresses to check for duplicates
     * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed
     */
    function createOperatorDirectedOperatorSetRewardsSubmission(
        OperatorSet calldata operatorSet,
        OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
    ) external;

    /**
     * @notice Claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
     * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,
     * they can simply claim against the latest root and the contract will calculate the difference between
     * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address.
     * @param claim The RewardsMerkleClaim to be processed.
     * Contains the root index, earner, token leaves, and required proofs
     * @param recipient The address recipient that receives the ERC20 rewards
     * @dev only callable by the valid claimer, that is
     * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only
     * claimerFor[claim.earner] can claim the rewards.
     */
    function processClaim(RewardsMerkleClaim calldata claim, address recipient) external;

    /**
     * @notice Batch claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
     * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,
     * they can simply claim against the latest root and the contract will calculate the difference between
     * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address.
     * @param claims The RewardsMerkleClaims to be processed.
     * Contains the root index, earner, token leaves, and required proofs
     * @param recipient The address recipient that receives the ERC20 rewards
     * @dev only callable by the valid claimer, that is
     * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only
     * claimerFor[claim.earner] can claim the rewards.
     * @dev This function may fail to execute with a large number of claims due to gas limits. Use a smaller array of claims if necessary.
     */
    function processClaims(RewardsMerkleClaim[] calldata claims, address recipient) external;

    /**
     * @notice Creates a new distribution root. activatedAt is set to block.timestamp + activationDelay
     * @param root The merkle root of the distribution
     * @param rewardsCalculationEndTimestamp The timestamp until which rewards have been calculated
     * @dev Only callable by the rewardsUpdater
     */
    function submitRoot(bytes32 root, uint32 rewardsCalculationEndTimestamp) external;

    /**
     * @notice allow the rewardsUpdater to disable/cancel a pending root submission in case of an error
     * @param rootIndex The index of the root to be disabled
     */
    function disableRoot(
        uint32 rootIndex
    ) external;

    /**
     * @notice Sets the address of the entity that can call `processClaim` on ehalf of an earner
     * @param claimer The address of the entity that can call `processClaim` on behalf of the earner
     * @dev Assumes msg.sender is the earner
     */
    function setClaimerFor(
        address claimer
    ) external;

    /**
     * @notice Sets the address of the entity that can call `processClaim` on behalf of an earner
     * @param earner The address to set the claimer for
     * @param claimer The address of the entity that can call `processClaim` on behalf of the earner
     * @dev Only callable by operators or AVSs. We define an AVS that has created at least one
     *      operatorSet in the `AllocationManager`
     */
    function setClaimerFor(address earner, address claimer) external;

    /**
     * @notice Sets the delay in timestamp before a posted root can be claimed against
     * @dev Only callable by the contract owner
     * @param _activationDelay The new value for activationDelay
     */
    function setActivationDelay(
        uint32 _activationDelay
    ) external;

    /**
     * @notice Sets the default split for all operators across all avss.
     * @param split The default split for all operators across all avss in bips.
     * @dev Only callable by the contract owner.
     */
    function setDefaultOperatorSplit(
        uint16 split
    ) external;

    /**
     * @notice Sets the split for a specific operator for a specific avs
     * @param operator The operator who is setting the split
     * @param avs The avs for which the split is being set by the operator
     * @param split The split for the operator for the specific avs in bips.
     * @dev Only callable by the operator
     * @dev Split has to be between 0 and 10000 bips (inclusive)
     * @dev The split will be activated after the activation delay
     */
    function setOperatorAVSSplit(address operator, address avs, uint16 split) external;

    /**
     * @notice Sets the split for a specific operator for Programmatic Incentives.
     * @param operator The operator on behalf of which the split is being set.
     * @param split The split for the operator for Programmatic Incentives in bips.
     * @dev Only callable by the operator
     * @dev Split has to be between 0 and 10000 bips (inclusive)
     * @dev The split will be activated after the activation delay
     */
    function setOperatorPISplit(address operator, uint16 split) external;

    /**
     * @notice Sets the split for a specific operator for a specific operatorSet.
     * @param operator The operator who is setting the split.
     * @param operatorSet The operatorSet for which the split is being set by the operator.
     * @param split The split for the operator for the specific operatorSet in bips.
     * @dev Only callable by the operator
     * @dev Split has to be between 0 and 10000 bips (inclusive)
     * @dev The split will be activated after the activation delay
     */
    function setOperatorSetSplit(address operator, OperatorSet calldata operatorSet, uint16 split) external;

    /**
     * @notice Sets the permissioned `rewardsUpdater` address which can post new roots
     * @dev Only callable by the contract owner
     * @param _rewardsUpdater The address of the new rewardsUpdater
     */
    function setRewardsUpdater(
        address _rewardsUpdater
    ) external;

    /**
     * @notice Sets the permissioned `rewardsForAllSubmitter` address which can submit createRewardsForAllSubmission
     * @dev Only callable by the contract owner
     * @param _submitter The address of the rewardsForAllSubmitter
     * @param _newValue The new value for isRewardsForAllSubmitter
     */
    function setRewardsForAllSubmitter(address _submitter, bool _newValue) external;

    /**
     *
     *                         VIEW FUNCTIONS
     *
     */

    /// @notice Delay in timestamp (seconds) before a posted root can be claimed against
    function activationDelay() external view returns (uint32);

    /// @notice The timestamp until which RewardsSubmissions have been calculated
    function currRewardsCalculationEndTimestamp() external view returns (uint32);

    /// @notice Mapping: earner => the address of the entity who can call `processClaim` on behalf of the earner
    function claimerFor(
        address earner
    ) external view returns (address);

    /// @notice Mapping: claimer => token => total amount claimed
    function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256);

    /// @notice the default split for all operators across all avss
    function defaultOperatorSplitBips() external view returns (uint16);

    /// @notice the split for a specific `operator` for a specific `avs`
    function getOperatorAVSSplit(address operator, address avs) external view returns (uint16);

    /// @notice the split for a specific `operator` for Programmatic Incentives
    function getOperatorPISplit(
        address operator
    ) external view returns (uint16);

    /// @notice Returns the split for a specific `operator` for a given `operatorSet`
    function getOperatorSetSplit(address operator, OperatorSet calldata operatorSet) external view returns (uint16);

    /// @notice return the hash of the earner's leaf
    function calculateEarnerLeafHash(
        EarnerTreeMerkleLeaf calldata leaf
    ) external pure returns (bytes32);

    /// @notice returns the hash of the earner's token leaf
    function calculateTokenLeafHash(
        TokenTreeMerkleLeaf calldata leaf
    ) external pure returns (bytes32);

    /// @notice returns 'true' if the claim would currently pass the check in `processClaims`
    /// but will revert if not valid
    function checkClaim(
        RewardsMerkleClaim calldata claim
    ) external view returns (bool);

    /// @notice returns the number of distribution roots posted
    function getDistributionRootsLength() external view returns (uint256);

    /// @notice returns the distributionRoot at the specified index
    function getDistributionRootAtIndex(
        uint256 index
    ) external view returns (DistributionRoot memory);

    /// @notice returns the current distributionRoot
    function getCurrentDistributionRoot() external view returns (DistributionRoot memory);

    /// @notice loop through the distribution roots from reverse and get latest root that is not disabled and activated
    /// i.e. a root that can be claimed against
    function getCurrentClaimableDistributionRoot() external view returns (DistributionRoot memory);

    /// @notice loop through distribution roots from reverse and return index from hash
    function getRootIndexFromHash(
        bytes32 rootHash
    ) external view returns (uint32);

    /// @notice The address of the entity that can update the contract with new merkle roots
    function rewardsUpdater() external view returns (address);

    /**
     * @notice The interval in seconds at which the calculation for a RewardsSubmission distribution is done.
     * @dev Rewards Submission durations must be multiples of this interval.
     */
    function CALCULATION_INTERVAL_SECONDS() external view returns (uint32);

    /// @notice The maximum amount of time (seconds) that a RewardsSubmission can span over
    function MAX_REWARDS_DURATION() external view returns (uint32);

    /// @notice max amount of time (seconds) that a submission can start in the past
    function MAX_RETROACTIVE_LENGTH() external view returns (uint32);

    /// @notice max amount of time (seconds) that a submission can start in the future
    function MAX_FUTURE_LENGTH() external view returns (uint32);

    /// @notice absolute min timestamp (seconds) that a submission can start at
    function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        if (!_safeTransfer(token, to, value, true)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        if (!_safeTransferFrom(token, from, to, value, true)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _safeTransfer(token, to, value, false);
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _safeTransferFrom(token, from, to, value, false);
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        if (!_safeApprove(token, spender, value, false)) {
            if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
            if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the
     * return value is optional (but if data is returned, it must not be false).
     *
     * @param token The token targeted by the call.
     * @param to The recipient of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
        bytes4 selector = IERC20.transfer.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(to, shr(96, not(0))))
            mstore(0x24, value)
            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
        }
    }

    /**
     * @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return
     * value: the return value is optional (but if data is returned, it must not be false).
     *
     * @param token The token targeted by the call.
     * @param from The sender of the tokens
     * @param to The recipient of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value,
        bool bubble
    ) private returns (bool success) {
        bytes4 selector = IERC20.transferFrom.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(from, shr(96, not(0))))
            mstore(0x24, and(to, shr(96, not(0))))
            mstore(0x44, value)
            success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
            mstore(0x60, 0)
        }
    }

    /**
     * @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:
     * the return value is optional (but if data is returned, it must not be false).
     *
     * @param token The token targeted by the call.
     * @param spender The spender of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
        bytes4 selector = IERC20.approve.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(spender, shr(96, not(0))))
            mstore(0x24, value)
            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

import {StorageSlot} from "./StorageSlot.sol";

/**
 * @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 EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * 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].
 *
 * IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
 * by the {ReentrancyGuardTransient} variant in v6.0.
 */
abstract contract ReentrancyGuard {
    using StorageSlot for bytes32;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    // 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;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _reentrancyGuardStorageSlot().getUint256Slot().value = 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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    /**
     * @dev A `view` only version of {nonReentrant}. Use to block view functions
     * from being called, preventing reading from inconsistent contract state.
     *
     * CAUTION: This is a "view" modifier and does not change the reentrancy
     * status. Use it only on view functions. For payable or non-payable functions,
     * use the standard {nonReentrant} modifier instead.
     */
    modifier nonReentrantView() {
        _nonReentrantBeforeView();
        _;
    }

    function _nonReentrantBeforeView() private view {
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        _nonReentrantBeforeView();

        // Any calls to nonReentrant after this point will fail
        _reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
    }

    function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
        return REENTRANCY_GUARD_STORAGE;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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]
 * ```solidity
 * 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 Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/

import {AuthLibrary} from "@aleph-vault/libraries/AuthLibrary.sol";
/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */

interface IAlephVaultDeposit {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when a new minimum deposit amount is queued.
     * @param classId The ID of the share class.
     * @param minDepositAmount The new minimum deposit amount.
     */
    event NewMinDepositAmountQueued(uint8 classId, uint256 minDepositAmount);

    /**
     * @notice Emitted when a new minimum user balance is queued.
     * @param classId The ID of the share class.
     * @param minUserBalance The new minimum user balance.
     */
    event NewMinUserBalanceQueued(uint8 classId, uint256 minUserBalance);

    /**
     * @notice Emitted when a new maximum deposit cap is queued.
     * @param classId The ID of the share class.
     * @param maxDepositCap The new maximum deposit cap.
     */
    event NewMaxDepositCapQueued(uint8 classId, uint256 maxDepositCap);

    /**
     * @notice Emitted when a new minimum deposit amount is set.
     * @param classId The ID of the share class.
     * @param minDepositAmount The new minimum deposit amount.
     */
    event NewMinDepositAmountSet(uint8 classId, uint256 minDepositAmount);

    /**
     * @notice Emitted when a new minimum user balance is set.
     * @param classId The ID of the share class.
     * @param minUserBalance The new minimum user balance.
     */
    event NewMinUserBalanceSet(uint8 classId, uint256 minUserBalance);

    /**
     * @notice Emitted when a new maximum deposit cap is set.
     * @param classId The ID of the share class.
     * @param maxDepositCap The new maximum deposit cap.
     */
    event NewMaxDepositCapSet(uint8 classId, uint256 maxDepositCap);

    /**
     * @notice Emitted when a deposit request is made.
     * @param classId The ID of the share class.
     * @param batchId The batch ID of the deposit request.
     * @param user The user making the deposit request.
     * @param amount The amount of the deposit request.
     */
    event DepositRequest(uint8 classId, uint48 batchId, address indexed user, uint256 amount);

    /**
     * @notice Emitted when a synchronous deposit is made.
     * @param classId The ID of the share class.
     * @param allocator The address making the deposit.
     * @param amount The amount deposited.
     * @param shares The number of shares minted.
     * @param seriesId The series ID where shares were minted.
     * @param batchId The batch ID when the deposit occurred.
     * @param totalAssets The total assets in the series after the deposit.
     * @param totalShares The total shares in the series after the deposit.
     */
    event SyncDeposit(
        uint8 indexed classId,
        address indexed allocator,
        uint256 amount,
        uint256 shares,
        uint32 seriesId,
        uint48 batchId,
        uint256 totalAssets,
        uint256 totalShares
    );

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the minimum deposit amount is invalid.
     */
    error InvalidMinDepositAmount();

    /**
     * @notice Emitted when the deposit is insufficient.
     */
    error InsufficientDeposit();

    /**
     * @notice Emitted when the deposit is less than the minimum deposit amount.
     */
    error DepositLessThanMinDepositAmount(uint256 minDepositAmount);

    /**
     * @notice Emitted when the deposit is less than the minimum user balance.
     */
    error DepositLessThanMinUserBalance(uint256 minUserBalance);

    /**
     * @notice Emitted when the deposit exceeds the maximum deposit cap.
     */
    error DepositExceedsMaxDepositCap(uint256 maxDepositCap);

    /**
     * @notice Emitted when only one request per batch is allowed for deposit.
     */
    error OnlyOneRequestPerBatchAllowedForDeposit();

    /**
     * @notice Emitted when the deposit request fails.
     */
    error DepositRequestFailed();

    /**
     * @notice Emitted when only async deposit is allowed (sync is not valid).
     */
    error OnlyAsyncDepositAllowed();

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Constructor params.
     * @param minDepositAmountTimelock The timelock period for the minimum deposit amount.
     * @param minUserBalanceTimelock The timelock period for the minimum user balance.
     * @param maxDepositCapTimelock The timelock period for the maximum deposit cap.
     */
    struct DepositConstructorParams {
        uint48 minDepositAmountTimelock;
        uint48 minUserBalanceTimelock;
        uint48 maxDepositCapTimelock;
    }

    /**
     * @notice Parameters for a deposit request.
     * @param classId The ID of the share class.
     * @param amount The amount of the deposit request.
     * @param _authSignature The auth signature for the deposit request.
     */
    struct RequestDepositParams {
        uint8 classId;
        uint256 amount;
        AuthLibrary.AuthSignature authSignature;
    }

    /*//////////////////////////////////////////////////////////////
                            TIMELOCK FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Queues a new minimum deposit amount.
     * @param _classId The ID of the share class to set the minimum deposit amount for.
     * @param _minDepositAmount The new minimum deposit amount.
     */
    function queueMinDepositAmount(uint8 _classId, uint256 _minDepositAmount) external;

    /**
     * @notice Queues a new minimum user balance.
     * @param _classId The ID of the share class to set the minimum user balance for.
     * @param _minUserBalance The new minimum user balance.
     */
    function queueMinUserBalance(uint8 _classId, uint256 _minUserBalance) external;

    /**
     * @notice Queues a new maximum deposit cap.
     * @param _classId The ID of the share class to set the maximum deposit cap for.
     * @param _maxDepositCap The new maximum deposit cap.
     */
    function queueMaxDepositCap(uint8 _classId, uint256 _maxDepositCap) external;

    /**
     * @notice Sets the minimum deposit amount.
     * @param _classId The ID of the share class to set the minimum deposit amount for.
     */
    function setMinDepositAmount(uint8 _classId) external;

    /**
     * @notice Sets the minimum user balance.
     * @param _classId The ID of the share class to set the minimum user balance for.
     */
    function setMinUserBalance(uint8 _classId) external;

    /**
     * @notice Sets the maximum deposit cap.
     * @param _classId The ID of the share class to set the maximum deposit cap for.
     */
    function setMaxDepositCap(uint8 _classId) external;

    /*//////////////////////////////////////////////////////////////
                            DEPOSIT FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Requests a deposit of assets into the vault for the current batch.
     * @param _requestDepositParams The parameters for the deposit request.
     * @return _batchId The batch ID for the deposit.
     */
    function requestDeposit(RequestDepositParams calldata _requestDepositParams) external returns (uint48 _batchId);

    /**
     * @notice Deposits assets synchronously into the vault, minting shares immediately.
     * @param _requestDepositParams The parameters for the deposit (same as requestDeposit).
     * @return _shares The number of shares minted to the caller.
     * @dev Only callable when totalAssets is valid for the specified class.
     */
    function syncDeposit(RequestDepositParams calldata _requestDepositParams) external returns (uint256 _shares);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/

import {IAlephVault} from "@aleph-vault/interfaces/IAlephVault.sol";

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
interface IAlephVaultFactory {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when a vault is deployed.
     * @param vault The address of the deployed vault.
     * @param manager The address of the manager.
     * @param name The name of the vault.
     * @param configId The config ID of the vault.
     */
    event VaultDeployed(address indexed vault, address indexed manager, string name, string configId);

    /**
     * @notice Emitted when the operations multisig is set.
     * @param operationsMultisig The new operations multisig.
     */
    event OperationsMultisigSet(address indexed operationsMultisig);

    /**
     * @notice Emitted when the oracle is set.
     * @param oracle The new oracle.
     */
    event OracleSet(address indexed oracle);

    /**
     * @notice Emitted when the guardian is set.
     * @param guardian The new guardian.
     */
    event GuardianSet(address indexed guardian);

    /**
     * @notice Emitted when the authentication signer is set.
     * @param authSigner The new authentication signer.
     */
    event AuthSignerSet(address indexed authSigner);

    /**
     * @notice Emitted when the management fee is set.
     * @param managementFee The new management fee.
     */
    event ManagementFeeSet(uint32 indexed managementFee);

    /**
     * @notice Emitted when the performance fee is set.
     * @param performanceFee The new performance fee.
     */
    event PerformanceFeeSet(uint32 indexed performanceFee);

    /**
     * @notice Emitted when the module implementation is set.
     * @param module The module identifier.
     * @param implementation The new implementation.
     */
    event ModuleImplementationSet(bytes4 indexed module, address indexed implementation);

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the initialization params are invalid.
     */
    error InvalidInitializationParams();

    /**
     * @notice Emitted when the parameter is invalid.
     */
    error InvalidParam();

    /**
     * @notice Emitted when the chain is unsupported.
     */
    error UnsupportedChain();

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Initialization params.
     * @param beacon The beacon address of the vault.
     * @param operationsMultisig The operations multisig address.
     * @param oracle The oracle address.
     * @param guardian The guardian address.
     * @param authSigner The authentication signer address.
     * @param accountant The accountant proxy address.
     * @param alephVaultDepositImplementation The aleph vault deposit implementation address.
     * @param alephVaultRedeemImplementation The aleph vault redeem implementation address.
     * @param alephVaultSettlementImplementation The aleph vault settlement implementation address.
     * @param feeManagerImplementation The fee manager implementation address.
     * @param migrationManagerImplementation The migration manager implementation address.
     */
    struct InitializationParams {
        address beacon;
        address operationsMultisig;
        address oracle;
        address guardian;
        address authSigner;
        address accountant;
        address alephVaultDepositImplementation;
        address alephVaultRedeemImplementation;
        address alephVaultSettlementImplementation;
        address feeManagerImplementation;
        address migrationManagerImplementation;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Checks if an address is a valid vault deployed by this factory
     * @param _vault The address to check
     * @return True if the vault was deployed by this factory, false otherwise
     */
    function isValidVault(address _vault) external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                            SETTER FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Updates the operations multisig address for the factory and all deployed vaults
     * @param _operationsMultisig The new operations multisig address
     * @dev Only callable by OPERATIONS_MULTISIG role. Updates all deployed vaults.
     */
    function setOperationsMultisig(address _operationsMultisig) external;

    /**
     * @notice Updates the oracle address for all deployed vaults
     * @param _oracle The new oracle address
     * @dev Only callable by OPERATIONS_MULTISIG role. Updates all deployed vaults.
     */
    function setOracle(address _oracle) external;

    /**
     * @notice Updates the guardian address for all deployed vaults
     * @param _guardian The new guardian address
     * @dev Only callable by OPERATIONS_MULTISIG role. Updates all deployed vaults.
     */
    function setGuardian(address _guardian) external;

    /**
     * @notice Updates the authentication signer address for vault deployment
     * @param _authSigner The new authentication signer address
     * @dev Only callable by OPERATIONS_MULTISIG role. Updates all deployed vaults.
     */
    function setAuthSigner(address _authSigner) external;

    /**
     * @notice Updates a module implementation address for all deployed vaults
     * @param _module The module identifier to update
     * @param _implementation The new implementation address for the module
     * @dev Only callable by OPERATIONS_MULTISIG role. Updates all deployed vaults.
     */
    function setModuleImplementation(bytes4 _module, address _implementation) external;

    /*//////////////////////////////////////////////////////////////
                            DEPLOY FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Deploys a new vault.
     * @param _userInitializationParams Struct containing all user initialization parameters.
     * @return The address of the new vault.
     */
    function deployVault(IAlephVault.UserInitializationParams calldata _userInitializationParams)
        external
        returns (address);
}

File 17 of 81 : IAlephVault.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/

import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import {AuthLibrary} from "@aleph-vault/libraries/AuthLibrary.sol";

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
interface IAlephVault {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the deposit authentication is enabled.
     */
    event IsDepositAuthEnabledSet(bool isDepositAuthEnabled);

    /**
     * @notice Emitted when the settlement authentication is enabled.
     */
    event IsSettlementAuthEnabledSet(bool isSettlementAuthEnabled);

    /**
     * @notice Emitted when the vault treasury is set.
     */
    event VaultTreasurySet(address vaultTreasury);

    /**
     * @notice Emitted when the share class is created.
     */
    event ShareClassCreated(uint8 classId, ShareClassParams shareClassParams);

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the initialization params are invalid.
     */
    error InvalidInitializationParams();

    /**
     * @notice Emitted when the auth signer is invalid.
     */
    error InvalidAuthSigner();

    /**
     * @notice Emitted when the share class is invalid.
     */
    error InvalidShareClass();

    /**
     * @notice Emitted when the share series is invalid.
     */
    error InvalidShareSeries();

    /**
     * @notice Emitted when the share class params are invalid.
     */
    error InvalidShareClassParams();

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Initialization params.
     * @param _operationsMultisig The operations multisig address.
     * @param _vaultFactory The vault factory address.
     * @param _manager The manager address.
     * @param _oracle The oracle address.
     * @param _guardian The guardian address.
     * @param _authSigner The auth signer address.
     * @param _accountant The accountant proxy address.
     * @param _userInitializationParams The user initialization params.
     * @param _moduleInitializationParams The module initialization params.
     */
    struct InitializationParams {
        address operationsMultisig;
        address vaultFactory;
        address manager;
        address oracle;
        address guardian;
        address authSigner;
        address accountant;
        UserInitializationParams userInitializationParams;
        ModuleInitializationParams moduleInitializationParams;
    }

    /**
     * @notice Initialization params provided by the user.
     * @param _name The name of the vault.
     * @param _configId The config ID of the vault.
     * @param _underlyingToken The underlying token address.
     * @param _custodian The custodian address in which vault funds are stored.
     * @param _vaultTreasury The vault treasury address in which fees are collected.
     * @param _syncExpirationBatches The number of batches sync flows remain valid after valuation update.
     * @param _shareClassParams The share class params for default share class.
     * @param _authSignature The auth signature to deploy the vault.
     */
    struct UserInitializationParams {
        string name;
        string configId;
        address underlyingToken;
        address custodian;
        address vaultTreasury;
        uint48 syncExpirationBatches;
        ShareClassParams shareClassParams;
        AuthLibrary.AuthSignature authSignature;
    }

    /**
     * @notice Initialization params for the modules.
     * @param _alephVaultDepositImplementation The aleph vault deposit implementation address.
     * @param _alephVaultRedeemImplementation The aleph vault redeem implementation address.
     * @param _alephVaultSettlementImplementation The aleph vault settlement implementation address.
     * @param _feeManagerImplementation The fee manager implementation address.
     * @param _migrationManagerImplementation The migration manager implementation address.
     */
    struct ModuleInitializationParams {
        address alephVaultDepositImplementation;
        address alephVaultRedeemImplementation;
        address alephVaultSettlementImplementation;
        address feeManagerImplementation;
        address migrationManagerImplementation;
    }

    /**
     * @notice Parameters for a share class.
     * @param _managementFee The management fee rate in basis points.
     * @param _performanceFee The performance fee rate in basis points.
     * @param _noticePeriod The notice period in batches.
     * @param _lockInPeriod The lock in period in batches.
     * @param _minDepositAmount The minimum deposit amount.
     * @param _minUserBalance The minimum user balance.
     * @param _maxDepositCap The maximum deposit cap.
     * @param _minRedeemAmount The minimum redeem amount.
     * @dev all amounts are denominated in underlying token decimals.
     */
    struct ShareClassParams {
        uint32 managementFee;
        uint32 performanceFee;
        uint48 noticePeriod;
        uint48 lockInPeriod;
        uint256 minDepositAmount;
        uint256 minUserBalance;
        uint256 maxDepositCap;
        uint256 minRedeemAmount;
    }

    /**
     * @notice Structure for a share class.
     * @param _shareSeriesId The number of share series created for the share class.
     * @param _lastConsolidatedSeriesId The ID of the last consolidated share series.
     * @param _lastFeePaidId The last Batch ID in which fees were paid.
     * @param _depositSettleId The last Batch ID in which deposits were settled.
     * @param _redeemSettleId The last Batch ID in which redemptions were settled.
     * @param _shareClassParams The parameters for the share class.
     * @param _shareSeries All share series for the share class.
     * @param _depositRequests The deposit requests made for the share class.
     * @param _redeemRequests The redemption requests made for the share class.
     * @param _userLockInPeriod The lock in period for each user in the share class.
     */
    struct ShareClass {
        uint32 shareSeriesId;
        uint32 lastConsolidatedSeriesId;
        uint48 lastFeePaidId;
        uint48 depositSettleId;
        uint48 redeemSettleId;
        ShareClassParams shareClassParams;
        mapping(uint32 => ShareSeries) shareSeries;
        mapping(uint48 batchId => DepositRequests) depositRequests;
        mapping(uint48 batchId => RedeemRequests) redeemRequests;
        mapping(address user => uint48) userLockInPeriod;
    }

    /**
     * @notice Structure for a share series.
     * @param _totalAssets The total assets in the share series.
     * @param _totalShares The total shares in the share series.
     * @param _highWaterMark The high water mark of the share series.
     * @param _users The users in the share series.
     * @param _sharesOf The shares of each user in the share series.
     * @dev assets and shares are denominated in underlying token decimals.
     * @dev if a share series is consolidated, the values inside this mapping are cleared out.
     */
    struct ShareSeries {
        uint256 totalAssets;
        uint256 totalShares;
        uint256 highWaterMark;
        EnumerableSet.AddressSet users;
        mapping(address => uint256) sharesOf;
    }

    /**
     * @notice Structure for the deposit requests for a share class.
     * @param _totalAmountToDeposit The total amount to deposit for the share class.
     * @param _usersToDeposit The users to deposit for the share class.
     * @param _depositRequest The deposit request for each user in the share class.
     * @dev deposit requests are amounts denominated in underlying token decimals.
     * @dev deposit request values are cleared out after settlement.
     */
    struct DepositRequests {
        uint256 totalAmountToDeposit;
        EnumerableSet.AddressSet usersToDeposit;
        mapping(address => uint256) depositRequest;
    }

    /**
     * @notice Structure for the redemption requests for a share class.
     * @param _usersToRedeem The users to redeem for the share class.
     * @param _redeemRequest The redemption request for each user in the share class.
     * @dev redemption requests are in share units (percentage of remaining total user assets in share class)
     * denominated in TOTAL_SHARE_UNITS (1e18).
     * @dev redemption request values are cleared out after settlement.
     */
    struct RedeemRequests {
        EnumerableSet.AddressSet usersToRedeem;
        mapping(address => uint256) redeemRequest;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Returns the start time of the vault.
     * @return The start time.
     */
    function startTimeStamp() external view returns (uint48);

    /**
     * @notice Returns the current batch ID based on the elapsed time since start.
     * @return The current batch ID.
     */
    function currentBatch() external view returns (uint48);

    /**
     * @notice Returns the number of share classes in the vault.
     * @return The number of share classes.
     */
    function shareClasses() external view returns (uint8);

    /**
     * @notice Returns the max series ID of the share class.
     * @param _classId The ID of the share class.
     * @return The max series ID of the share class.
     */
    function shareSeriesId(uint8 _classId) external view returns (uint32);

    /**
     * @notice Returns the last consolidated series ID of the share class.
     * @param _classId The ID of the share class.
     * @return The last consolidated series ID of the share class.
     */
    function lastConsolidatedSeriesId(uint8 _classId) external view returns (uint32);

    /**
     * @notice Returns the name of the vault.
     * @return The name.
     */
    function name() external view returns (string memory);

    /**
     * @notice Returns the manager of the vault.
     * @return The manager.
     */
    function manager() external view returns (address);

    /**
     * @notice Returns the underlying token of the vault.
     * @return The underlying token.
     */
    function underlyingToken() external view returns (address);

    /**
     * @notice Returns the custodian of the vault.
     * @return The custodian.
     */
    function custodian() external view returns (address);

    /**
     * @notice Returns the vault treasury of the vault.
     * @return The vault treasury.
     */
    function vaultTreasury() external view returns (address);

    /**
     * @notice Checks if total assets are valid for synchronous operations for a specific class.
     * @param _classId The share class ID to check.
     * @return true if sync flows are allowed, false otherwise.
     */
    function isTotalAssetsValid(uint8 _classId) external view returns (bool);

    /**
     * @notice Returns the operations multisig of the vault.
     * @return The operations multisig.
     */
    function operationsMultisig() external view returns (address);

    /**
     * @notice Returns the oracle of the vault.
     * @return The oracle.
     */
    function oracle() external view returns (address);

    /**
     * @notice Returns the guardian of the vault.
     * @return The guardian.
     */
    function guardian() external view returns (address);

    /**
     * @notice Returns the KYC authentication signer of the vault.
     * @return The KYC authentication signer.
     */
    function authSigner() external view returns (address);

    /**
     * @notice Returns the accountant of the vault.
     * @return The accountant.
     */
    function accountant() external view returns (address);

    /**
     * @notice Returns the management fee of the vault.
     * @param _classId The ID of the share class.
     * @return The management fee.
     */
    function managementFee(uint8 _classId) external view returns (uint32);

    /**
     * @notice Returns the performance fee of the vault.
     * @param _classId The ID of the share class.
     * @return The performance fee.
     */
    function performanceFee(uint8 _classId) external view returns (uint32);

    /**
     * @notice Returns the notice period of the vault.
     * @param _classId The ID of the share class.
     * @return The notice period.
     */
    function noticePeriod(uint8 _classId) external view returns (uint48);

    /**
     * @notice Returns the lock in period of the vault.
     * @param _classId The ID of the share class.
     * @return The lock in period.
     */
    function lockInPeriod(uint8 _classId) external view returns (uint48);

    /**
     * @notice Returns the minimum deposit amount.
     * @param _classId The ID of the share class.
     * @return The minimum deposit amount of the share class.
     */
    function minDepositAmount(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the minimum user balance.
     * @param _classId The ID of the share class.
     * @return The minimum user balance of the share class.
     */
    function minUserBalance(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the maximum deposit cap.
     * @param _classId The ID of the share class.
     * @return The maximum deposit cap of the share class.
     */
    function maxDepositCap(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the minimum redeem amount.
     * @param _classId The ID of the share class.
     * @return The minimum redeem amount of the share class.
     */
    function minRedeemAmount(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the user lock in period.
     * @param _classId The ID of the share class.
     * @param _user The address of the user.
     * @return The user lock in period.
     */
    function userLockInPeriod(uint8 _classId, address _user) external view returns (uint48);

    /**
     * @notice Returns the amount of assets claimable by a user.
     * @param _user The address of the user.
     * @return The amount of assets claimable by the user.
     */
    function redeemableAmount(address _user) external view returns (uint256);

    /**
     * @notice Returns whether authentication is enabled for deposits.
     * @return The status of the authentication for deposits.
     */
    function isDepositAuthEnabled() external view returns (bool);

    /**
     * @notice Returns whether authentication is enabled for settlements.
     * @return The status of the authentication for settlements.
     */
    function isSettlementAuthEnabled() external view returns (bool);

    /**
     * @notice Returns the total assets currently held by the vault.
     * @return The total assets.
     */
    function totalAssets() external view returns (uint256);

    /**
     * @notice Returns the total assets in the vault for a given class.
     * @param _classId The ID of the share class.
     * @return The total assets in the vault for the given class.
     */
    function totalAssetsOfClass(uint8 _classId) external view returns (uint256[] memory);

    /**
     * @notice Returns the total assets in the vault for a given class.
     * @param _classId The ID of the share class.
     * @return The total assets in the vault for the given class.
     */
    function totalAssetsPerClass(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the total assets in the vault for a given series.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @return The total assets in the vault for the given series.
     */
    function totalAssetsPerSeries(uint8 _classId, uint32 _seriesId) external view returns (uint256);

    /**
     * @notice Returns the total shares in the vault for a given series.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @return The total shares in the vault for the given series.
     */
    function totalSharesPerSeries(uint8 _classId, uint32 _seriesId) external view returns (uint256);

    /**
     * @notice Returns the total assets of a user in a given class.
     * @param _classId The ID of the share class.
     * @param _user The address of the user.
     * @return The total assets of a user in a given class.
     */
    function assetsPerClassOf(uint8 _classId, address _user) external view returns (uint256);

    /**
     * @notice Returns the amount of assets claimable by a user based on their shares.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @param _user The address of the user.
     * @return The amount of assets claimable by the user.
     */
    function assetsOf(uint8 _classId, uint32 _seriesId, address _user) external view returns (uint256);

    /**
     * @notice Returns the number of shares owned by a user.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @param _user The address of the user.
     * @return The number of shares owned by the user.
     */
    function sharesOf(uint8 _classId, uint32 _seriesId, address _user) external view returns (uint256);

    /**
     * @notice Returns the current price per share of the vault.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @return The current price per share.
     */
    function pricePerShare(uint8 _classId, uint32 _seriesId) external view returns (uint256);

    /**
     * @notice Returns the current high water mark of the vault.
     * @param _classId The ID of the share class.
     * @param _seriesId The ID of the share series.
     * @return The current high water mark.
     */
    function highWaterMark(uint8 _classId, uint32 _seriesId) external view returns (uint256);

    /**
     * @notice Returns the total amount of unsettled deposit requests for a given class.
     * @param _classId The ID of the share class.
     * @return The total amount of unsettled deposit requests for the given class.
     * @dev Please note that this function will return the deposit amount for all batches including the current batch.
     * However, if these deposit requests are settled in this batch, the amount requested in this batch will NOT be settled.
     * It will be settled in the next settlement batch. So if you're using this function to check for the deposit request for settlement,
     * please be aware of this nuance.
     */
    function totalAmountToDeposit(uint8 _classId) external view returns (uint256);

    /**
     * @notice Returns the total amount of unsettled deposit requests for a given class at a given batch.
     * @param _classId The ID of the share class.
     * @param _batchId The ID of the batch.
     * @return The total amount of unsettled deposit requests for the given class at the given batch.
     */
    function totalAmountToDepositAt(uint8 _classId, uint48 _batchId) external view returns (uint256);

    /**
     * @notice Returns the deposit request of a user.
     * @param _classId The ID of the share class.
     * @param _user The user to query.
     * @return The deposit request of the user.
     */
    function depositRequestOf(uint8 _classId, address _user) external view returns (uint256);

    /**
     * @notice Returns the deposit request of a user at a given batch.
     * @param _classId The ID of the share class.
     * @param _user The user to query.
     * @param _batchId The ID of the batch.
     * @return The deposit request of the user at the given batch.
     */
    function depositRequestOfAt(uint8 _classId, address _user, uint48 _batchId) external view returns (uint256);

    /**
     * @notice Returns the redeem request of a user.
     * @param _classId The ID of the share class.
     * @param _user The user to query.
     * @return The redeem request of the user.
     */
    function redeemRequestOf(uint8 _classId, address _user) external view returns (uint256);

    /**
     * @notice Returns the redeem request of a user at a given batch.
     * @param _classId The ID of the share class.
     * @param _user The user to query.
     * @param _batchId The ID of the batch.
     * @return The redeem request of the user at the given batch.
     */
    function redeemRequestOfAt(uint8 _classId, address _user, uint48 _batchId) external view returns (uint256);

    /**
     * @notice Returns the users to deposit at a given batch.
     * @param _classId The ID of the share class.
     * @param _batchId The ID of the batch.
     * @return The users to deposit at the given batch.
     */
    function usersToDepositAt(uint8 _classId, uint48 _batchId) external view returns (address[] memory);

    /**
     * @notice Returns the users to redeem at a given batch.
     * @param _classId The ID of the share class.
     * @param _batchId The ID of the batch.
     * @return The users to redeem at the given batch.
     */
    function usersToRedeemAt(uint8 _classId, uint48 _batchId) external view returns (address[] memory);

    /**
     * @notice Returns the total fee amount to collect.
     * @return The total fee amount to collect.
     */
    function totalFeeAmountToCollect() external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                            SETTER FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Sets whether authentication is enabled for deposits.
     * @param _isDepositAuthEnabled The new status of the authentication for deposits.
     */
    function setIsDepositAuthEnabled(bool _isDepositAuthEnabled) external;

    /**
     * @notice Sets whether authentication is enabled for settlements.
     * @param _isSettlementAuthEnabled The new status of the authentication for settlements.
     */
    function setIsSettlementAuthEnabled(bool _isSettlementAuthEnabled) external;

    /**
     * @notice Sets the vault treasury.
     * @param _vaultTreasury The new vault treasury.
     */
    function setVaultTreasury(address _vaultTreasury) external;

    /**
     * @notice Creates a new share class.
     * @param _shareClassParams The parameters for the share class.
     */
    function createShareClass(ShareClassParams memory _shareClassParams) external returns (uint8 _classId);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
interface IAlephVaultRedeem {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when a new notice period is queued.
     * @param classId The ID of the share class.
     * @param noticePeriod The new notice period in batches.
     */
    event NewNoticePeriodQueued(uint8 classId, uint48 noticePeriod);

    /**
     * @notice Emitted when a new lock in period is queued.
     * @param classId The ID of the share class.
     * @param lockInPeriod The new lock in period in batches.
     */
    event NewLockInPeriodQueued(uint8 classId, uint48 lockInPeriod);

    /**
     * @notice Emitted when a new minimum redeem amount is queued.
     * @param classId The ID of the share class.
     * @param minRedeemAmount The new minimum redeem amount.
     */
    event NewMinRedeemAmountQueued(uint8 classId, uint256 minRedeemAmount);

    /**
     * @notice Emitted when a new notice period is set.
     * @param classId The ID of the share class.
     * @param noticePeriod The new notice period in batches.
     */
    event NewNoticePeriodSet(uint8 classId, uint48 noticePeriod);

    /**
     * @notice Emitted when a new lock in period is set.
     * @param classId The ID of the share class.
     * @param lockInPeriod The new lock in period in batches.
     */
    event NewLockInPeriodSet(uint8 classId, uint48 lockInPeriod);

    /**
     * @notice Emitted when a new minimum redeem amount is set.
     * @param classId The ID of the share class.
     * @param minRedeemAmount The new minimum redeem amount.
     */
    event NewMinRedeemAmountSet(uint8 classId, uint256 minRedeemAmount);

    /**
     * @notice Emitted when a redeem request is made.
     * @param classId The ID of the share class.
     * @param batchId The batch ID of the redeem request.
     * @param user The user making the redeem request.
     * @param estAmountToRedeem The estimated amount to redeem.
     */
    event RedeemRequest(uint8 classId, uint48 batchId, address indexed user, uint256 estAmountToRedeem);

    /**
     * @notice Emitted when the redeemable amount is withdrawn.
     * @param user The user withdrawing the redeemable amount.
     * @param redeemableAmount The redeemable amount.
     */
    event RedeemableAmountWithdrawn(address indexed user, uint256 redeemableAmount);

    /**
     * @notice Emitted when the excess assets are withdrawn.
     * @param excessAssets The excess assets.
     */
    event ExcessAssetsWithdrawn(uint256 excessAssets);

    /**
     * @notice Emitted when a synchronous redeem is made.
     * @param classId The ID of the share class.
     * @param batchId The batch ID when the redeem occurred.
     * @param allocator The address making the redeem.
     * @param requestedAmount The amount requested to redeem.
     */
    event SyncRedeem(uint8 indexed classId, uint48 batchId, address indexed allocator, uint256 requestedAmount);

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the minimum redeem amount is invalid.
     */
    error InvalidMinRedeemAmount();

    /**
     * @notice Emitted when the redeem is less than the minimum redeem amount.
     * @param minRedeemAmount The minimum redeem amount.
     */
    error RedeemLessThanMinRedeemAmount(uint256 minRedeemAmount);

    /**
     * @notice Emitted when the user is in the lock in period.
     * @param userLockInPeriod The user lock in period.
     */
    error UserInLockInPeriodNotElapsed(uint48 userLockInPeriod);

    /**
     * @notice Emitted when the assets to redeem are insufficient.
     */
    error InsufficientAssetsToRedeem();

    /**
     * @notice Emitted when the redeem falls below the minimum user balance.
     * @param minUserBalance The minimum user balance.
     */
    error RedeemFallBelowMinUserBalance(uint256 minUserBalance);

    /**
     * @notice Emitted when only one request per batch is allowed for redeem.
     */
    error OnlyOneRequestPerBatchAllowedForRedeem();

    /**
     * @notice Emitted when the vault balance is insufficient.
     */
    error InsufficientVaultBalance();

    /**
     * @notice Emitted when only async redeem is allowed (sync is not valid).
     */
    error OnlyAsyncRedeemAllowed();

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Constructor params.
     * @param noticePeriodTimelock The timelock period for the notice period.
     * @param lockInPeriodTimelock The timelock period for the lock in period.
     * @param minRedeemAmountTimelock The timelock period for the minimum redeem amount.
     */
    struct RedeemConstructorParams {
        uint48 noticePeriodTimelock;
        uint48 lockInPeriodTimelock;
        uint48 minRedeemAmountTimelock;
    }

    /**
     * @notice Parameters for a redeem request.
     * @param classId The ID of the share class.
     * @param estAmountToRedeem The estimated amount to redeem.
     * @dev pleas note that the amount mentioned here is used to calculate the share units based on the
     * PPS at the moment of request. The actual amount the users can withdraw after settlement may be
     * different from the amount specified here due to change in PPS based on the pnl of the vault and fees.
     */
    struct RedeemRequestParams {
        uint8 classId;
        uint256 estAmountToRedeem;
    }

    /*//////////////////////////////////////////////////////////////
                            TIMELOCK FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Queues a new notice period.
     * @param _classId The ID of the share class to set the notice period for.
     * @param _noticePeriod The new notice period in batches.
     */
    function queueNoticePeriod(uint8 _classId, uint48 _noticePeriod) external;

    /**
     * @notice Queues a new lock in period.
     * @param _classId The ID of the share class to set the lock in period for.
     * @param _lockInPeriod The new lock in period in batches.
     */
    function queueLockInPeriod(uint8 _classId, uint48 _lockInPeriod) external;

    /**
     * @notice Queues a new minimum redeem amount.
     * @param _classId The ID of the share class to set the minimum redeem amount for.
     * @param _minRedeemAmount The new minimum redeem amount.
     */
    function queueMinRedeemAmount(uint8 _classId, uint256 _minRedeemAmount) external;

    /**
     * @notice Sets the notice period in batches
     * @param _classId The ID of the share class to set the notice period for.
     */
    function setNoticePeriod(uint8 _classId) external;

    /**
     * @notice Sets the lock in period in batches
     * @param _classId The ID of the share class to set the lock in period for.
     */
    function setLockInPeriod(uint8 _classId) external;

    /**
     * @notice Sets the minimum redeem amount.
     * @param _classId The ID of the share class to set the minimum redeem amount for.
     */
    function setMinRedeemAmount(uint8 _classId) external;

    /*//////////////////////////////////////////////////////////////
                            REDEEM FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Requests to redeem shares from the vault for the current batch.
     * @param _redeemRequestParams The parameters for the redeem request.
     * @return _batchId The batch ID for the redeem request.
     */
    function requestRedeem(RedeemRequestParams calldata _redeemRequestParams) external returns (uint48 _batchId);

    /**
     * @notice Withdraws the redeemable amount for the user.
     */
    function withdrawRedeemableAmount() external;

    /**
     * @notice Withdraws excess assets from the vault and sends back to custodian.
     */
    function withdrawExcessAssets() external;

    /**
     * @notice Redeems shares synchronously from the vault, transferring assets immediately.
     * @param _redeemRequestParams The parameters for the redeem (same as requestRedeem).
     * @return _assets The amount of assets transferred to the caller.
     * @dev Only callable when totalAssets is valid. Bypasses notice period.
     */
    function syncRedeem(RedeemRequestParams calldata _redeemRequestParams) external returns (uint256 _assets);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/
import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
interface IAccountant {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the operations multisig is set.
     * @param operationsMultisig The new operations multisig.
     */
    event OperationsMultisigSet(address operationsMultisig);

    /**
     * @notice Emitted when the vault factory is set.
     * @param vaultFactory The new vault factory.
     */
    event VaultFactorySet(address vaultFactory);

    /**
     * @notice Emitted when the aleph treasury is set.
     * @param alephTreasury The new aleph treasury.
     */
    event AlephTreasurySet(address alephTreasury);

    /**
     * @notice Emitted when the vault treasury is set.
     * @param vault The vault.
     * @param vaultTreasury The new vault treasury.
     */
    event VaultTreasurySet(address vault, address vaultTreasury);

    /**
     * @notice Emitted when the management fee cut is set.
     * @param vault The vault.
     * @param managementFeeCut The new management fee cut.
     */
    event ManagementFeeCutSet(address vault, uint32 managementFeeCut);

    /**
     * @notice Emitted when the performance fee cut is set.
     * @param vault The vault.
     * @param performanceFeeCut The new performance fee cut.
     */
    event PerformanceFeeCutSet(address vault, uint32 performanceFeeCut);

    /**
     * @notice Emitted when the operator fee cut is set.
     * @param vault The vault.
     * @param operatorFeeCut The new operator fee cut.
     */
    event OperatorFeeCutSet(address vault, uint32 operatorFeeCut);

    /**
     * @notice Emitted when the operator allocations are set.
     * @param vault The vault.
     * @param operator The operator.
     * @param allocatedAmount The allocated amount.
     */
    event OperatorAllocationsSet(address vault, address operator, uint256 allocatedAmount);

    /**
     * @notice Emitted when operator fees are distributed to an operator.
     * @param vault The vault.
     * @param operator The operator receiving the fee.
     * @param operatorFee The fee amount distributed to the operator.
     */
    event OperatorFeeDistributed(address vault, address operator, uint256 operatorFee);

    /**
     * @notice Emitted when fees are collected.
     * @param vault The vault.
     * @param managementFeesToCollect The management fees to collect.
     * @param performanceFeesToCollect The performance fees to collect.
     * @param vaultFee The vault fee split
     * @param alephFee The aleph fee split
     * @param operatorsFee The fees for the operators.
     */
    event FeesCollected(
        address vault,
        uint256 managementFeesToCollect,
        uint256 performanceFeesToCollect,
        uint256 vaultFee,
        uint256 alephFee,
        uint256[] operatorsFee
    );

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when the initialization params are invalid.
     */
    error InvalidInitializationParams();

    /**
     * @notice Emitted when the vault is invalid.
     */
    error InvalidVault();

    /**
     * @notice Emitted when the manager is invalid.
     */
    error InvalidManager();

    /**
     * @notice Emitted when the vault treasury is invalid.
     */
    error InvalidVaultTreasury();

    /**
     * @notice Emitted when the vault treasury is not set.
     */
    error VaultTreasuryNotSet();

    /**
     * @notice Emitted when fees are not collected.
     */
    error FeesNotCollected();

    /**
     * @notice Emitted when the operator fee cut is invalid.
     */
    error InvalidOperatorFeeCut();

    /**
     * @notice Emitted when the operator allocation input is invalid.
     */
    error InvalidOperatorAllocation();

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Initialization params.
     * @param operationsMultisig The operations multisig.
     * @param alephTreasury The aleph treasury.
     */
    struct InitializationParams {
        address operationsMultisig;
        address alephTreasury;
    }

    /**
     * @notice Operator allocations.
     * @param totalOperatorAllocations The total operator allocations.
     * @param operators The operators.
     * @param allocatedAmount The allocated amount by each operator.
     */
    struct OperatorAllocations {
        uint256 totalOperatorAllocations;
        EnumerableSet.AddressSet operators;
        mapping(address operator => uint256 allocatedAmount) allocatedAmount;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Returns the vault treasury of the caller.
     * @return The vault treasury.
     */
    function vaultTreasury() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                            SETTER FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Initializes the vault treasury.
     * @param _vault The vault to initialize the treasury for.
     * @param _vaultTreasury The new vault treasury.
     */
    function initializeVaultTreasury(address _vault, address _vaultTreasury) external;

    /**
     * @notice Sets the operations multisig.
     * @param _operationsMultisig The new operations multisig.
     */
    function setOperationsMultisig(address _operationsMultisig) external;

    /**
     * @notice Sets the vault factory.
     * @param _vaultFactory The new vault factory.
     */
    function setVaultFactory(address _vaultFactory) external;

    /**
     * @notice Sets the aleph treasury.
     * @param _alephTreasury The new aleph treasury.
     */
    function setAlephTreasury(address _alephTreasury) external;

    /**
     * @notice Sets the vault treasury.
     * @param _vaultTreasury The new vault treasury.
     */
    function setVaultTreasury(address _vaultTreasury) external;

    /**
     * @notice Sets the aleph avs.
     * @param _alephAvs The new aleph avs.
     */
    function setAlephAvs(address _alephAvs) external;

    /**
     * @notice Sets the management fee cut.
     * @param _vault The vault to set the management fee cut for.
     * @param _managementFeeCut The new management fee cut.
     */
    function setManagementFeeCut(address _vault, uint32 _managementFeeCut) external;

    /**
     * @notice Sets the performance fee cut.
     * @param _vault The vault to set the performance fee cut for.
     * @param _performanceFeeCut The new performance fee cut.
     */
    function setPerformanceFeeCut(address _vault, uint32 _performanceFeeCut) external;

    /**
     * @notice Sets the operator fee cut.
     * @param _vault The vault to set the operator fee cut for.
     * @param _operatorFeeCut The new operator fee cut.
     */
    function setOperatorFeeCut(address _vault, uint32 _operatorFeeCut) external;

    /**
     * @notice Sets the operator allocations.
     * @param _vault The vault to set the operator allocations for.
     * @param _operator The operator to set the allocations for.
     * @param _allocatedAmount The new allocated amount.
     */
    function setOperatorAllocations(address _vault, address _operator, uint256 _allocatedAmount) external;

    /*//////////////////////////////////////////////////////////////
                            FEE FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Collects all pending fees from a given vault.
     * @param _vault The vault to collect fees from.
     */
    function collectFees(address _vault) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/**
 * @title IERC20Factory
 * @notice Interface for ERC20 token factory
 */
interface IERC20Factory {
    /**
     * @notice Emitted when a new ERC20 token is created
     * @param token The address of the newly created token
     * @param name The name of the token
     * @param symbol The symbol of the token
     * @param creator The address that created the token (msg.sender of createToken)
     */
    event TokenCreated(address indexed token, string name, string symbol, address indexed creator);

    /**
     * @notice Creates a new ERC20 token
     * @param name The name of the token
     * @param symbol The symbol of the token
     * @param decimals The number of decimals for the token
     * @return token The address of the newly created token
     */
    function createToken(string memory name, string memory symbol, uint8 decimals) external returns (address token);

    /**
     * @notice Gets the number of tokens created by this factory
     * @return count The number of tokens created
     */
    function getTokenCount() external view returns (uint256 count);

    /**
     * @notice Gets the address of a token by its index
     * @param index The index of the token
     * @return token The address of the token
     */
    function getToken(uint256 index) external view returns (address token);

    /**
     * @notice Checks if an address is a token created by this factory
     * @param token The address to check
     * @return isToken Whether the address is a token created by this factory
     */
    function isToken(address token) external view returns (bool isToken);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {IERC20Factory} from "./interfaces/IERC20Factory.sol";
import {ERC20Token} from "./ERC20Token.sol";
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";

/**
 * @title ERC20Factory
 * @notice Factory contract for creating slashed ERC20 tokens
 * @dev Creates mintable/burnable ERC20 tokens for representing vault shares
 */
contract ERC20Factory is IERC20Factory, Ownable {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error EmptyString();
    error IndexOutOfBounds();

    /*//////////////////////////////////////////////////////////////
                        NAMESPACED STORAGE (ERC-7201)
    //////////////////////////////////////////////////////////////*/

    /// @custom:storage-location erc7201:erc20.factory.storage
    struct FactoryStorage {
        /// @dev Array of all tokens created by this factory
        address[] tokens;
        /// @dev Mapping from token address to creation status
        mapping(address => bool) isToken;
    }

    // keccak256(abi.encode(uint256(keccak256("erc20.factory.storage")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant FACTORY_STORAGE_LOCATION =
        0x5c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b900;

    function _getFactoryStorage() private pure returns (FactoryStorage storage $) {
        assembly {
            $.slot := FACTORY_STORAGE_LOCATION
        }
    }

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Modifier to ensure the string is not empty
     * @param _str The string to check
     */
    modifier nonEmptyString(string memory _str) {
        if (bytes(_str).length == 0) revert EmptyString();
        _;
    }

    /**
     * @notice Modifier to ensure the index is within bounds
     * @param _index The index to check
     */
    modifier validIndex(uint256 _index) {
        if (_index >= _getFactoryStorage().tokens.length) revert IndexOutOfBounds();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Constructs the ERC20Factory contract
     * @param _owner The owner of the contract
     */
    constructor(address _owner) Ownable(_owner) {}

    /*//////////////////////////////////////////////////////////////
                          PUBLIC FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Creates a new ERC20 token
     * @param name The name of the token
     * @param symbol The symbol of the token
     * @param decimals The number of decimals for the token (typically 18)
     * @return token The address of the newly created token
     */
    function createToken(string memory name, string memory symbol, uint8 decimals)
        external
        override
        onlyOwner
        nonEmptyString(name)
        nonEmptyString(symbol)
        returns (address token)
    {
        // Deploy new token
        ERC20Token newToken = new ERC20Token(name, symbol, decimals, msg.sender);
        token = address(newToken);

        // Track the token
        FactoryStorage storage $ = _getFactoryStorage();
        $.tokens.push(token);
        $.isToken[token] = true;

        // Emit event
        emit TokenCreated(token, name, symbol, msg.sender);

        return token;
    }

    /**
     * @notice Gets the number of tokens created by this factory
     * @return count The number of tokens created
     */
    function getTokenCount() external view override returns (uint256 count) {
        return _getFactoryStorage().tokens.length;
    }

    /**
     * @notice Gets the address of a token by its index
     * @param index The index of the token
     * @return token The address of the token
     */
    function getToken(uint256 index) external view override validIndex(index) returns (address token) {
        return _getFactoryStorage().tokens[index];
    }

    /**
     * @notice Gets all tokens created by this factory
     * @return tokens Array of all token addresses
     */
    function getAllTokens() external view returns (address[] memory tokens) {
        return _getFactoryStorage().tokens;
    }

    /**
     * @notice Checks if an address is a token created by this factory
     * @param token The address to check
     * @return Whether the address is a token created by this factory
     */
    function isToken(address token) external view override returns (bool) {
        return _getFactoryStorage().isToken[token];
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAlephAVSPausable} from "./interfaces/IAlephAVSPausable.sol";
import {AccessControlUpgradeable} from "@openzeppelin-upgrades/contracts/access/AccessControlUpgradeable.sol";

contract AlephAVSPausable is IAlephAVSPausable, AccessControlUpgradeable {
    bytes4 public constant ALLOCATE_FLOW = bytes4(keccak256("ALLOCATE_FLOW"));
    bytes4 public constant UNALLOCATE_FLOW = bytes4(keccak256("UNALLOCATE_FLOW"));

    /*//////////////////////////////////////////////////////////////
                        NAMESPACED STORAGE (ERC-7201)
    //////////////////////////////////////////////////////////////*/

    /// @custom:storage-location erc7201:aleph.avs.storage
    struct PausableStorage {
        // Mapping of pausable flow to its pause state
        mapping(bytes4 pausableFlow => bool isPaused) flowsPauseStates;
    }

    // keccak256(abi.encode(uint256(keccak256("pausable.avs.storage")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PAUSABLE_STORAGE_LOCATION =
        0xf8131bc7f7376b7d8d09601d142eed8304a0dce3fa238f43875ec194c35e4400;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PAUSABLE_STORAGE_LOCATION
        }
    }

    /*//////////////////////////////////////////////////////////////
                                MODIFIERS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Modifier to check if a flow is not paused
     * @param _pausableFlow The flow identifier
     */
    modifier whenFlowNotPaused(bytes4 _pausableFlow) {
        _revertIfFlowPaused(_pausableFlow);
        _;
    }

    /**
     * @notice Modifier to check if a flow is paused
     * @param _pausableFlow The flow identifier
     */
    modifier whenFlowPaused(bytes4 _pausableFlow) {
        _revertIfFlowUnpaused(_pausableFlow);
        _;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    function isFlowPaused(bytes4 _pausableFlow) external view returns (bool _isPaused) {
        return _getPausableStorage().flowsPauseStates[_pausableFlow];
    }

    /*//////////////////////////////////////////////////////////////
                            PAUSING FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    function pause(bytes4 _pausableFlow) external onlyRole(_pausableFlow) {
        _pause(_pausableFlow);
    }

    function unpause(bytes4 _pausableFlow) external onlyRole(_pausableFlow) {
        _unpause(_pausableFlow);
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @dev Internal function to pause a flow
     * @param _pausableFlow The flow identifier
     */
    function _pause(bytes4 _pausableFlow) internal {
        PausableStorage storage _sd = _getPausableStorage();
        if (_sd.flowsPauseStates[_pausableFlow]) revert FlowIsCurrentlyPaused();

        _sd.flowsPauseStates[_pausableFlow] = true;
        emit FlowPaused(_pausableFlow, msg.sender);
    }

    /**
     * @dev Internal function to unpause a flow
     * @param _pausableFlow The flow identifier
     */
    function _unpause(bytes4 _pausableFlow) internal {
        PausableStorage storage _sd = _getPausableStorage();
        if (!_sd.flowsPauseStates[_pausableFlow]) revert FlowIsCurrentlyUnpaused();

        _sd.flowsPauseStates[_pausableFlow] = false;
        emit FlowUnpaused(_pausableFlow, msg.sender);
    }

    /**
     * @dev Internal function to revert if a flow is paused
     * @param _pausableFlow The flow identifier
     */
    function _revertIfFlowPaused(bytes4 _pausableFlow) internal view {
        if (_getPausableStorage().flowsPauseStates[_pausableFlow]) revert FlowIsCurrentlyPaused();
    }

    /**
     * @dev Internal function to revert if a flow is unpaused
     * @param _pausableFlow The flow identifier
     */
    function _revertIfFlowUnpaused(bytes4 _pausableFlow) internal view {
        if (!_getPausableStorage().flowsPauseStates[_pausableFlow]) revert FlowIsCurrentlyUnpaused();
    }

    /**
     * @dev Internal function to initialize the pausable storage
     * @param _owner The owner address
     * @param _guardian The guardian address
     */
    function _pausableInit(address _owner, address _guardian) internal {
        _grantRole(ALLOCATE_FLOW, _owner);
        _grantRole(ALLOCATE_FLOW, _guardian);
        _grantRole(UNALLOCATE_FLOW, _owner);
        _grantRole(UNALLOCATE_FLOW, _guardian);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

/**
 * @title IMintableBurnableERC20
 * @notice Interface for ERC20 tokens that support minting and burning
 */
interface IMintableBurnableERC20 {
    /**
     * @notice Mints new tokens to the specified address
     * @param to The address to receive the tokens
     * @param amount The amount of tokens to mint
     */
    function mint(address to, uint256 amount) external;

    /**
     * @notice Burns tokens from the specified address
     * @param from The address to burn tokens from
     * @param amount The amount of tokens to burn
     */
    function burn(address from, uint256 amount) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {AlephUtils} from "./AlephUtils.sol";

/**
 * @title AlephSlashing
 * @notice Library for slashing-related calculations and operations
 * @dev Provides functions for calculating magnitudes, wads, and executing slashes
 */
library AlephSlashing {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error AmountTooSmall();
    error InsufficientAllocation();
    error MagnitudeOverflow();

    /*//////////////////////////////////////////////////////////////
                    MAGNITUDE CALCULATION FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Calculate magnitude to slash from underlying token amount
     * @param _allocationManager The AllocationManager contract
     * @param _operator The operator address
     * @param _operatorSet The operator set
     * @param _amount The underlying token amount
     * @param _strategy The strategy
     * @return magnitudeToSlash The magnitude to slash
     */
    function calculateMagnitudeFromAmount(
        IAllocationManager _allocationManager,
        address _operator,
        OperatorSet memory _operatorSet,
        uint256 _amount,
        IStrategy _strategy
    ) internal view returns (uint64 magnitudeToSlash) {
        uint256 sharesNeeded = _strategy.underlyingToSharesView(_amount);
        if (sharesNeeded == 0) revert AmountTooSmall();

        uint256 allocatedShares = getAllocatedShares(_allocationManager, _operator, _operatorSet, _strategy);
        if (allocatedShares < sharesNeeded) revert InsufficientAllocation();

        IAllocationManagerTypes.Allocation memory allocation =
            _allocationManager.getAllocation(_operator, _operatorSet, _strategy);
        uint256 magnitudeUint = Math.mulDiv(uint256(allocation.currentMagnitude), sharesNeeded, allocatedShares);

        if (magnitudeUint > uint256(allocation.currentMagnitude)) {
            magnitudeUint = uint256(allocation.currentMagnitude);
        }
        if (magnitudeUint > type(uint64).max) revert MagnitudeOverflow();

        // casting to 'uint64' is safe because we check magnitudeUint <= type(uint64).max above
        // forge-lint: disable-next-line(unsafe-typecast)
        magnitudeToSlash = uint64(magnitudeUint);
    }

    /**
     * @notice Get allocated shares for an operator in an operator set
     * @param _allocationManager The AllocationManager contract
     * @param _operator The operator address
     * @param _operatorSet The operator set
     * @param _strategy The strategy
     * @return The allocated shares
     */
    function getAllocatedShares(
        IAllocationManager _allocationManager,
        address _operator,
        OperatorSet memory _operatorSet,
        IStrategy _strategy
    ) internal view returns (uint256) {
        address[] memory operators = new address[](1);
        operators[0] = _operator;
        IStrategy[] memory strategyArray = AlephUtils.asStrategyArray(_strategy);
        uint256[][] memory allocatedStakes =
            _allocationManager.getAllocatedStake(_operatorSet, operators, strategyArray);
        return allocatedStakes[0][0];
    }

    /**
     * @notice Verify operator has allocated magnitude to the operator set
     * @param _allocationManager The AllocationManager contract
     * @param _operator The operator address
     * @param _operatorSet The operator set
     * @param _magnitudeToSlash The magnitude to slash
     * @param _strategy The strategy
     */
    function verifyOperatorAllocation(
        IAllocationManager _allocationManager,
        address _operator,
        OperatorSet memory _operatorSet,
        uint64 _magnitudeToSlash,
        IStrategy _strategy
    ) internal view {
        IAllocationManagerTypes.Allocation memory allocation =
            _allocationManager.getAllocation(_operator, _operatorSet, _strategy);
        if (allocation.currentMagnitude < _magnitudeToSlash) revert InsufficientAllocation();
    }

    /*//////////////////////////////////////////////////////////////
                        SLASHING EXECUTION FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Execute slash of operator to redistribute shares
     * @param _allocationManager The AllocationManager contract
     * @param _avsAddress The AVS address (slash caller)
     * @param _operator The operator to slash
     * @param _operatorSetId The operator set ID
     * @param _magnitudeToSlash The magnitude to slash
     * @param _strategy The strategy being slashed
     * @param _slashDescription The description for the slash
     * @return slashId The ID of the slash
     */
    function executeSlashAndGetId(
        IAllocationManager _allocationManager,
        address _avsAddress,
        address _operator,
        uint32 _operatorSetId,
        uint64 _magnitudeToSlash,
        IStrategy _strategy,
        string memory _slashDescription
    ) internal returns (uint256 slashId) {
        OperatorSet memory operatorSet = AlephUtils.getOperatorSet(_avsAddress, _operatorSetId);
        IAllocationManagerTypes.Allocation memory allocation =
            _allocationManager.getAllocation(_operator, operatorSet, _strategy);
        uint256 wadToSlash = calculateWadToSlash(_magnitudeToSlash, allocation.currentMagnitude);

        IStrategy[] memory strategyArray = AlephUtils.asStrategyArray(_strategy);
        uint256[] memory wadsToSlash = new uint256[](1);
        wadsToSlash[0] = wadToSlash;

        IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({
            operator: _operator,
            operatorSetId: _operatorSetId,
            strategies: strategyArray,
            wadsToSlash: wadsToSlash,
            description: _slashDescription
        });

        (slashId,) = _allocationManager.slashOperator(_avsAddress, slashingParams);
        return slashId;
    }

    /**
     * @notice Calculate wad to slash from magnitude and current magnitude
     * @param _magnitudeToSlash The magnitude to slash
     * @param _currentMagnitude The current magnitude of the allocation
     * @return The wad (proportion) to slash, capped at 1e18 (100%)
     */
    function calculateWadToSlash(uint64 _magnitudeToSlash, uint64 _currentMagnitude) internal pure returns (uint256) {
        if (_currentMagnitude == 0 || _magnitudeToSlash == 0) return 0;

        uint64 _magnitudeToSlashAdjusted = _magnitudeToSlash > _currentMagnitude ? _currentMagnitude : _magnitudeToSlash;

        uint256 _wad = Math.mulDiv(uint256(_magnitudeToSlashAdjusted), AlephUtils.WAD, uint256(_currentMagnitude));

        return _wad > AlephUtils.WAD ? AlephUtils.WAD : _wad;
    }

    /**
     * @notice Clear redistributable shares and receive tokens
     * @param _strategyManager The StrategyManager contract
     * @param _avsAddress The AVS address
     * @param _operatorSetId The operator set ID
     * @param _slashId The slash ID
     * @param _strategy The strategy
     * @return The token amount received
     */
    function clearRedistributableShares(
        IStrategyManager _strategyManager,
        address _avsAddress,
        uint32 _operatorSetId,
        uint256 _slashId,
        IStrategy _strategy
    ) internal returns (uint256) {
        OperatorSet memory operatorSet = AlephUtils.getOperatorSet(_avsAddress, _operatorSetId);
        return _strategyManager.clearBurnOrRedistributableSharesByStrategy(operatorSet, _slashId, _strategy);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20 as IERC20Eigen} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC4626Math} from "Aleph/src/libraries/ERC4626Math.sol";
import {IAlephVault} from "Aleph/src/interfaces/IAlephVault.sol";
import {IAlephVaultDeposit} from "Aleph/src/interfaces/IAlephVaultDeposit.sol";
import {IAlephVaultRedeem} from "Aleph/src/interfaces/IAlephVaultRedeem.sol";
import {IAlephVaultFactory} from "Aleph/src/interfaces/IAlephVaultFactory.sol";
import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IERC20Factory} from "../interfaces/IERC20Factory.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {IMintableBurnableERC20} from "../interfaces/IMintableBurnableERC20.sol";
import {AlephUtils} from "./AlephUtils.sol";
import {AlephValidation} from "./AlephValidation.sol";

/**
 * @title AlephVaultManagement
 * @notice Library for vault management operations
 * @dev Provides functions for vault interactions, slashed token/strategy creation, deposits, withdrawals, and calculations
 */
library AlephVaultManagement {
    using SafeERC20 for IERC20;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error InvalidAlephVault();

    /*//////////////////////////////////////////////////////////////
                        VAULT TOKEN/STRATEGY HELPERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Gets the vault token
     * @param _vaultFactory The Aleph Vault Factory
     * @param _vault The vault address
     * @return vaultToken The vault's underlying token
     */
    function getVaultToken(IAlephVaultFactory _vaultFactory, address _vault) internal view returns (IERC20 vaultToken) {
        AlephValidation.validateVault(_vaultFactory, _vault);
        vaultToken = IERC20(address(IAlephVault(_vault).underlyingToken()));
    }

    /**
     * @notice Gets the vault token and validates the provided strategy
     * @param _vaultFactory The Aleph Vault Factory
     * @param _vault The vault address
     * @param _strategy The strategy to validate (must match vault's underlying token)
     * @return vaultToken The vault's underlying token
     */
    function getVaultTokenAndStrategy(IAlephVaultFactory _vaultFactory, address _vault, IStrategy _strategy)
        internal
        view
        returns (IERC20 vaultToken)
    {
        vaultToken = getVaultToken(_vaultFactory, _vault);
        AlephValidation.validateStrategy(address(_strategy));
        if (address(_strategy.underlyingToken()) != address(vaultToken)) {
            revert AlephValidation.InvalidStrategy();
        }
    }

    /**
     * @notice Creates or gets the slashed token for a vault
     * @param _erc20Factory The ERC20 Factory
     * @param _vault The vault address
     * @param _originalToken The original token (vault's underlying token)
     * @return slashedToken The slashed token address
     */
    function createSlashedTokenForVault(IERC20Factory _erc20Factory, address _vault, IERC20 _originalToken)
        internal
        returns (IERC20 slashedToken)
    {
        // Create new slashed token
        // Naming convention: "al" prefix stands for "Aleph"
        // Format: "al<OriginalTokenName>-<VaultName>"
        string memory _vaultName = IAlephVault(_vault).name();
        string memory _originalName = IERC20Metadata(address(_originalToken)).name();
        string memory _originalSymbol = IERC20Metadata(address(_originalToken)).symbol();

        string memory _tokenName = string.concat("al", _originalName, "-", _vaultName);
        string memory _tokenSymbol = string.concat("al", _originalSymbol, "-", _vaultName);

        address _slashedTokenAddress =
            _erc20Factory.createToken(_tokenName, _tokenSymbol, IERC20Metadata(address(_originalToken)).decimals());

        slashedToken = IERC20(_slashedTokenAddress);
    }

    /**
     * @notice Creates or gets the slashed strategy for a vault
     * @param _strategyFactory The Strategy Factory
     * @param _vault The vault address
     * @param _originalToken The original token (vault's underlying token)
     * @param _erc20Factory The ERC20 Factory
     * @return slashedToken The slashed token
     * @return slashedStrategy The slashed strategy
     */
    function createSlashedStrategyForVault(
        IStrategyFactory _strategyFactory,
        address _vault,
        IERC20 _originalToken,
        mapping(address => IStrategy) storage _vaultToSlashedStrategy,
        IERC20Factory _erc20Factory
    ) internal returns (IERC20 slashedToken, IStrategy slashedStrategy) {
        slashedToken = createSlashedTokenForVault(_erc20Factory, _vault, _originalToken);

        // Convert to IERC20Eigen for StrategyFactory (uses @openzeppelin/contracts IERC20)
        // Note: StrategyFactory expects IERC20 from @openzeppelin/contracts, which is IERC20Eigen
        IERC20Eigen _slashedTokenEigen = IERC20Eigen(address(slashedToken));

        // Check if a strategy already exists for this token (edge case: partial transaction)
        // This should never happen with CREATE, but we handle it gracefully
        IStrategy _existingStrategy = _strategyFactory.deployedStrategies(_slashedTokenEigen);

        if (address(_existingStrategy) != address(0)) {
            // Strategy already exists - reuse it (this handles edge cases from partial transactions)
            slashedStrategy = _existingStrategy;
        } else {
            // Deploy a new strategy for the newly created slashed token
            slashedStrategy = _strategyFactory.deployNewStrategy(_slashedTokenEigen);
        }

        // Store in mapping for easy querying
        _vaultToSlashedStrategy[_vault] = slashedStrategy;
    }

    /*//////////////////////////////////////////////////////////////
                        VAULT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Deposits tokens to an Aleph vault synchronously
     * @param _vault The vault address
     * @param _requestDepositParams The deposit parameters
     * @param _tokenAmount The token amount to deposit (may differ from _requestDepositParams.amount due to rounding)
     * @return shares The number of shares minted
     */
    function depositToAlephVault(
        address _vault,
        IAlephVaultDeposit.RequestDepositParams calldata _requestDepositParams,
        uint256 _tokenAmount
    ) internal returns (uint256 shares) {
        IERC20 _vaultToken = IERC20(IAlephVault(_vault).underlyingToken());
        SafeERC20.forceApprove(_vaultToken, _vault, _tokenAmount);

        IAlephVaultDeposit.RequestDepositParams memory adjustedParams = IAlephVaultDeposit.RequestDepositParams({
            classId: _requestDepositParams.classId,
            amount: _tokenAmount,
            authSignature: _requestDepositParams.authSignature
        });

        if (!IAlephVault(_vault).isTotalAssetsValid(_requestDepositParams.classId)) {
            IAlephVaultDeposit(_vault).requestDeposit(adjustedParams);
            shares = 0;
        } else {
            shares = IAlephVaultDeposit(_vault).syncDeposit(adjustedParams);
        }
        SafeERC20.forceApprove(_vaultToken, _vault, 0);
    }

    /**
     * @notice Calculates the amount of slashed tokens to mint based on the amount to deposit
     * @param _classId The share class ID
     * @param _vault The vault address
     * @param _slashedToken The slashed token address
     * @param _amount The amount deposited to the vault
     * @return The amount of slashed tokens to mint
     */
    function calculateAmountToMint(uint8 _classId, address _vault, address _slashedToken, uint256 _amount)
        internal
        view
        returns (uint256)
    {
        return ERC4626Math.previewDeposit(
            _amount, IERC20(_slashedToken).totalSupply(), IAlephVault(_vault).assetsPerClassOf(_classId, address(this))
        );
    }

    /**
     * @notice Calculates the amount of tokens to redeem based on the amount of slashed tokens to burn
     * @param _classId The share class ID
     * @param _vault The vault address
     * @param _slashedToken The slashed token address
     * @param _amount The amount of slashed tokens to burn
     * @return The amount of tokens to redeem
     */
    function calculateAmountToRedeem(uint8 _classId, address _vault, address _slashedToken, uint256 _amount)
        internal
        view
        returns (uint256)
    {
        return ERC4626Math.previewRedeem(
            _amount, IAlephVault(_vault).assetsPerClassOf(_classId, address(this)), IERC20(_slashedToken).totalSupply()
        );
    }

    /**
     * @notice Redeems tokens from the vault with calculated parameters
     * @param _vault The vault address
     * @param _slashedStrategy The slashed strategy
     * @param _amount The amount of slashed tokens to redeem
     * @param _classId The share class ID
     * @return amount The amount of tokens redeemed
     */
    function redeemFromVault(address _vault, IStrategy _slashedStrategy, uint256 _amount, uint8 _classId)
        internal
        returns (uint256 amount)
    {
        uint256 _estAmountToRedeem =
            calculateAmountToRedeem(_classId, _vault, address(_slashedStrategy.underlyingToken()), _amount);
        IAlephVaultRedeem.RedeemRequestParams memory _redeemParams =
            IAlephVaultRedeem.RedeemRequestParams({classId: _classId, estAmountToRedeem: _estAmountToRedeem});
        return IAlephVaultRedeem(_vault).syncRedeem(_redeemParams);
    }

    /*//////////////////////////////////////////////////////////////
                        SLASHED TOKEN OPERATIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Validates balance and burns slashed tokens
     * @param _tokenHolder The token holder address
     * @param _slashedStrategy The slashed strategy
     * @param _tokenAmount The amount to burn
     * @param _contractAddress The contract address to transfer tokens to
     */
    function validateAndBurnSlashedTokens(
        address _tokenHolder,
        IStrategy _slashedStrategy,
        uint256 _tokenAmount,
        address _contractAddress
    ) internal {
        IMintableBurnableERC20 _slashedToken = IMintableBurnableERC20(address(_slashedStrategy.underlyingToken()));
        IERC20 _token = IERC20(address(_slashedToken));
        if (_token.balanceOf(_tokenHolder) < _tokenAmount) revert InsufficientBalance();

        _token.safeTransferFrom(_tokenHolder, _contractAddress, _tokenAmount);
        _slashedToken.burn(_contractAddress, _tokenAmount);
    }

    error InsufficientBalance();

    /*//////////////////////////////////////////////////////////////
                        STRATEGY DEPOSIT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Deposits tokens back into the original strategy on behalf of a staker
     * @param _strategyManager The StrategyManager contract
     * @param _targetStrategy The target strategy to deposit to (from initializeVault)
     * @param _token The token to deposit
     * @param _staker The staker address
     * @param _tokenAmount The token amount to deposit
     * @param _expiry The expiry timestamp for the signature
     * @param _signature The staker's signature
     * @return shares The shares received in the strategy
     */
    function depositToOriginalStrategy(
        IStrategyManager _strategyManager,
        IStrategy _targetStrategy,
        IERC20 _token,
        address _staker,
        uint256 _tokenAmount,
        uint256 _expiry,
        bytes calldata _signature
    ) internal returns (uint256 shares) {
        AlephValidation.validateStrategy(address(_targetStrategy));

        SafeERC20.forceApprove(_token, address(_strategyManager), _tokenAmount);
        shares = _strategyManager.depositIntoStrategyWithSignature(
            _targetStrategy, IERC20Eigen(address(_token)), _tokenAmount, _staker, _expiry, _signature
        );
        SafeERC20.forceApprove(_token, address(_strategyManager), 0);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC20 as IERC20Eigen} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IRewardsCoordinatorTypes} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {AlephUtils} from "./AlephUtils.sol";

/**
 * @title RewardsManagement
 * @notice Library for managing rewards submissions to EigenLayer's RewardsCoordinator
 * @dev Handles operator-directed rewards for vault allocations
 */
library RewardsManagement {
    using SafeERC20 for IERC20;

    /*//////////////////////////////////////////////////////////////
                            CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev Number of intervals to look back for retroactive rewards (EigenLayer requirement)
    uint256 private constant RETROACTIVE_INTERVALS = 2;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error OperatorSplitNotZero();

    /**
     * @notice Submits operator-directed rewards to RewardsCoordinator
     * @param _rewardsCoordinator The RewardsCoordinator contract
     * @param _avsAddress The AVS address submitting rewards
     * @param _operator The operator receiving rewards
     * @param _stakerStrategy The strategy used for reward calculation
     * @param _rewardToken The token to distribute as rewards
     * @param _rewardAmount The amount of rewards to distribute
     * @param _rewardsDescription The description for the rewards submission
     * @dev Creates a retroactive rewards submission that covers the previous calculation interval
     */
    function submitOperatorDirectedRewards(
        IRewardsCoordinator _rewardsCoordinator,
        address _avsAddress,
        address _operator,
        IStrategy _stakerStrategy,
        IERC20Eigen _rewardToken,
        uint256 _rewardAmount,
        string memory _rewardsDescription
    ) internal {
        IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory strategiesAndMultipliers =
            createStrategyAndMultiplierArray(_stakerStrategy);
        IRewardsCoordinatorTypes.OperatorReward[] memory operatorRewards =
            createOperatorRewardArray(_operator, _rewardAmount);
        (uint32 _startTimestamp, uint32 _duration) = calculateRetroactiveRewardsWindow(_rewardsCoordinator);

        IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission[] memory rewardsSubmissions =
            createRewardsSubmissionArray(
                strategiesAndMultipliers, _rewardToken, operatorRewards, _startTimestamp, _duration, _rewardsDescription
            );

        // Approve RewardsCoordinator to spend tokens using SafeERC20
        SafeERC20.forceApprove(IERC20(address(_rewardToken)), address(_rewardsCoordinator), _rewardAmount);
        _rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission(_avsAddress, rewardsSubmissions);
        // Reset approval to 0 after use
        SafeERC20.forceApprove(IERC20(address(_rewardToken)), address(_rewardsCoordinator), 0);
    }

    /**
     * @notice Creates a single-element StrategyAndMultiplier array
     * @param _strategy The strategy
     * @return Array containing the strategy with default multiplier
     */
    function createStrategyAndMultiplierArray(IStrategy _strategy)
        internal
        pure
        returns (IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory)
    {
        IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory arr =
            new IRewardsCoordinatorTypes.StrategyAndMultiplier[](1);
        arr[0] = IRewardsCoordinatorTypes.StrategyAndMultiplier({
            strategy: _strategy, multiplier: AlephUtils.REWARD_MULTIPLIER
        });
        return arr;
    }

    /**
     * @notice Creates a single-element OperatorReward array
     * @param _operator The operator
     * @param _amount The reward amount
     * @return Array containing the operator reward
     */
    function createOperatorRewardArray(address _operator, uint256 _amount)
        internal
        pure
        returns (IRewardsCoordinatorTypes.OperatorReward[] memory)
    {
        IRewardsCoordinatorTypes.OperatorReward[] memory arr = new IRewardsCoordinatorTypes.OperatorReward[](1);
        arr[0] = IRewardsCoordinatorTypes.OperatorReward({operator: _operator, amount: _amount});
        return arr;
    }

    /**
     * @notice Creates a single-element OperatorDirectedRewardsSubmission array
     * @param _strategiesAndMultipliers The strategies and multipliers
     * @param _token The reward token
     * @param _operatorRewards The operator rewards
     * @param _startTimestamp The start timestamp
     * @param _duration The duration
     * @param _description The description
     * @return Array containing the rewards submission
     */
    function createRewardsSubmissionArray(
        IRewardsCoordinatorTypes.StrategyAndMultiplier[] memory _strategiesAndMultipliers,
        IERC20Eigen _token,
        IRewardsCoordinatorTypes.OperatorReward[] memory _operatorRewards,
        uint32 _startTimestamp,
        uint32 _duration,
        string memory _description
    ) internal pure returns (IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission[] memory) {
        IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission[] memory arr =
            new IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission[](1);
        arr[0] = IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission({
            strategiesAndMultipliers: _strategiesAndMultipliers,
            token: _token,
            operatorRewards: _operatorRewards,
            startTimestamp: _startTimestamp,
            duration: _duration,
            description: _description
        });
        return arr;
    }

    /**
     * @notice Calculates retroactive rewards window for EigenLayer submission
     * @param _rewardsCoordinator The RewardsCoordinator contract
     * @return startTimestamp The start timestamp for the rewards window
     * @return duration The duration of the rewards window (one interval)
     * @dev Uses RETROACTIVE_INTERVALS to ensure rewards are based on pre-slash share amounts
     */
    function calculateRetroactiveRewardsWindow(IRewardsCoordinator _rewardsCoordinator)
        internal
        view
        returns (uint32 startTimestamp, uint32 duration)
    {
        uint32 interval = _rewardsCoordinator.CALCULATION_INTERVAL_SECONDS();
        startTimestamp = uint32(((block.timestamp / interval) - RETROACTIVE_INTERVALS) * interval);
        duration = interval;
    }

    /**
     * @notice Validates that operator's AVS split is 0 (100% to stakers)
     * @param _rewardsCoordinator The RewardsCoordinator contract
     * @param _operator The operator address
     * @param _avsAddress The AVS address
     * @dev getOperatorAVSSplit returns a uint16 value in basis points (bips):
     *      - Range: 0 to 10,000 (inclusive)
     *      - 0 = 0% to operator, 100% to stakers (required)
     *      - 10,000 = 100% to operator, 0% to stakers
     *      - If operator hasn't set a custom split, returns defaultOperatorSplitBips (typically 1000 = 10%)
     *      - Reverts if the split is not 0, ensuring all rewards go to stakers
     */
    function validateOperatorSplit(IRewardsCoordinator _rewardsCoordinator, address _operator, address _avsAddress)
        internal
        view
    {
        uint16 operatorSplit = _rewardsCoordinator.getOperatorAVSSplit(_operator, _avsAddress);
        if (operatorSplit != 0) {
            revert OperatorSplitNotZero();
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAlephVaultRedeem} from "Aleph/src/interfaces/IAlephVaultRedeem.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

/**
 * @title UnallocateManagement
 * @notice Library for managing unallocate operations
 * @dev Handles calculation logic for unallocate operations to reduce contract size
 */
library UnallocateManagement {
    /**
     * @notice Calculates the user's proportional share for complete unallocate
     * @dev Calculates share based on proportional distribution. If user is the last one or calculated
     *      share exceeds available, they receive all remaining withdrawn amount.
     * @param _userPendingAmount The user's pending unallocation amount
     * @param _totalPending The total pending unallocation amount across all users
     * @param _totalRedeemableAmount The total redeemable amount (vault redeemable + previously withdrawn)
     * @param _withdrawnAmount The currently withdrawn amount stored in contract
     * @return _userShare The user's calculated share amount
     */
    function calculateUserShare(
        uint256 _userPendingAmount,
        uint256 _totalPending,
        uint256 _totalRedeemableAmount,
        uint256 _withdrawnAmount
    ) internal pure returns (uint256 _userShare) {
        if (_totalPending == 0 || _userPendingAmount == 0) {
            return 0;
        }

        // Calculate user's share: (userPending / totalPending) * totalRedeemable
        _userShare = (_userPendingAmount * _totalRedeemableAmount) / _totalPending;

        // If this is the last user (or only user), they get all remaining withdrawn amount
        if (_totalPending == _userPendingAmount || _userShare > _withdrawnAmount) {
            _userShare = _withdrawnAmount > 0 ? _withdrawnAmount : _totalRedeemableAmount;
        }
    }

    /**
     * @notice Calculates the expected amount for complete unallocate (pure view function)
     * @dev This is a pure function that calculates the expected amount before any state changes.
     *      Used for signature generation. Handles proportional distribution and edge cases.
     * @param _userPendingAmount The user's pending unallocation amount
     * @param _totalPending The total pending unallocation amount across all users
     * @param _vaultRedeemableAmount The current redeemable amount available in the vault
     * @param _withdrawnAmount The amount previously withdrawn and stored in contract
     * @return _expectedAmount The expected amount the user will receive (0 if no pending or no redeemable)
     */
    function calculateCompleteUnallocateAmountView(
        uint256 _userPendingAmount,
        uint256 _totalPending,
        uint256 _vaultRedeemableAmount,
        uint256 _withdrawnAmount
    ) internal pure returns (uint256 _expectedAmount) {
        if (_userPendingAmount == 0) {
            return 0;
        }

        uint256 _totalRedeemableAmount = _vaultRedeemableAmount + _withdrawnAmount;
        if (_totalRedeemableAmount == 0 || _totalPending == 0) {
            return 0;
        }

        // Calculate expected share: (userPending / totalPending) * totalRedeemable
        _expectedAmount = (_userPendingAmount * _totalRedeemableAmount) / _totalPending;

        // Available amount is what can actually be withdrawn:
        // - If vault has new redeemable: available = vaultRedeemable + withdrawn (will be withdrawn in completeUnallocate)
        // - If vault has no new redeemable: available = withdrawn (already withdrawn)
        uint256 _availableAmount = _vaultRedeemableAmount > 0 ? _totalRedeemableAmount : _withdrawnAmount;

        // If this is the last user (or only user), they get all remaining available amount
        // Also if calculated share exceeds available, cap to available (last user gets remainder)
        if (_totalPending == _userPendingAmount) {
            // Last user gets all available
            _expectedAmount = _availableAmount;
        } else if (_expectedAmount > _availableAmount) {
            // Calculated share exceeds available, cap to available
            _expectedAmount = _availableAmount;
        }
    }

    /**
     * @notice Withdraws redeemable amount from vault and calculates total available
     * @dev Withdraws new redeemable funds if available, otherwise uses previously withdrawn amount.
     *      Verifies actual contract balance when using previously withdrawn funds.
     * @param _alephVault The vault address to withdraw from
     * @param _vaultToken The vault token contract
     * @param _vaultRedeemableAmount The current redeemable amount available in the vault
     * @param _withdrawnAmount The amount previously withdrawn and stored in contract state
     * @return _totalRedeemableAmount The total amount available (newly withdrawn + previously withdrawn)
     * @custom:reverts If no funds are available (should be checked before calling)
     */
    function withdrawAndCalculateAvailable(
        address _alephVault,
        IERC20 _vaultToken,
        uint256 _vaultRedeemableAmount,
        uint256 _withdrawnAmount
    ) internal returns (uint256 _totalRedeemableAmount) {
        if (_vaultRedeemableAmount > 0) {
            // Withdraw new redeemable amount from vault
            IAlephVaultRedeem(_alephVault).withdrawRedeemableAmount();
            _totalRedeemableAmount = _vaultRedeemableAmount + _withdrawnAmount;
        } else if (_withdrawnAmount > 0) {
            // Use previously withdrawn amount, but check actual contract balance
            uint256 _contractBalance = _vaultToken.balanceOf(address(this));
            _totalRedeemableAmount = _contractBalance < _withdrawnAmount ? _contractBalance : _withdrawnAmount;
        } else {
            // No funds available - this should be caught before calling this function
            revert();
        }
    }

    /**
     * @notice Calculates the final amount to withdraw, capping to available balances
     * @dev Caps the calculated share to both available amount and contract balance to ensure
     *      we never attempt to withdraw more than what's actually available.
     * @param _calculatedShare The user's calculated proportional share
     * @param _availableAmount The amount available for withdrawal (vault-specific)
     * @param _contractBalance The contract's current token balance (global safety check)
     * @return _finalAmount The final amount to withdraw (capped to minimum of share, available, and balance)
     * @custom:reverts If final amount is zero after capping
     */
    function calculateFinalAmount(uint256 _calculatedShare, uint256 _availableAmount, uint256 _contractBalance)
        internal
        pure
        returns (uint256 _finalAmount)
    {
        _finalAmount = _calculatedShare;

        // Cap to available amount
        if (_finalAmount > _availableAmount) {
            _finalAmount = _availableAmount;
        }

        // Cap to contract balance
        if (_finalAmount > _contractBalance) {
            _finalAmount = _contractBalance;
        }

        if (_finalAmount == 0) {
            revert();
        }
    }

    /**
     * @notice Calculates new storage values after processing unallocation
     * @dev Pure function that calculates what storage values should be after completing an unallocation.
     *      Reduces withdrawn amount by the amount used, reduces total pending by user's pending amount.
     *      Clears withdrawn amount if no more pending unallocations remain.
     * @param _currentVaultWithdrawnAmount Current total withdrawn amount stored for this vault
     * @param _amount The amount being withdrawn and used in this unallocation
     * @param _userPendingAmount The user's pending unallocation amount being processed
     * @param _totalPending The total pending unallocation amount across all users
     * @return _newVaultWithdrawnAmount New vault withdrawn amount (current - amount used, or 0 if no pending left)
     * @return _newTotalPending New total pending amount (total - user's pending, or 0 if invalid)
     */
    function calculateUnallocationStorageUpdates(
        uint256 _currentVaultWithdrawnAmount,
        uint256 _amount,
        uint256 _userPendingAmount,
        uint256 _totalPending
    ) internal pure returns (uint256 _newVaultWithdrawnAmount, uint256 _newTotalPending) {
        // Update vault withdrawn amount: subtract the amount being used
        _newVaultWithdrawnAmount = _currentVaultWithdrawnAmount - _amount;

        // Update total pending: subtract user's pending amount
        if (_userPendingAmount > _totalPending) {
            _newTotalPending = 0;
        } else {
            _newTotalPending = _totalPending - _userPendingAmount;
        }

        // If no more pending unallocations, clear withdrawn amount
        if (_newTotalPending == 0 && _newVaultWithdrawnAmount > 0) {
            _newVaultWithdrawnAmount = 0;
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
import {IAlephVaultFactory} from "Aleph/src/interfaces/IAlephVaultFactory.sol";
import {AlephUtils} from "./AlephUtils.sol";

/**
 * @title AlephValidation
 * @notice Library for validation logic across Aleph contracts
 * @dev Provides validation functions for operator sets, vaults, and strategies
 */
library AlephValidation {
    /*//////////////////////////////////////////////////////////////
                                   ERRORS
    //////////////////////////////////////////////////////////////*/

    error InvalidOperatorSet();
    error NotMemberOfOperatorSet();
    error InvalidAlephVault();
    error InvalidStrategy();

    /**
     * @notice Validates operator set and membership
     * @param _allocationManager The AllocationManager contract
     * @param _operator The operator address
     * @param _operatorSet The operator set
     */
    function validateOperatorSetAndMembership(
        IAllocationManager _allocationManager,
        address _operator,
        OperatorSet memory _operatorSet
    ) internal view {
        if (!_allocationManager.isOperatorSet(_operatorSet)) revert InvalidOperatorSet();
        if (!_allocationManager.isMemberOfOperatorSet(_operator, _operatorSet)) revert NotMemberOfOperatorSet();
    }

    /*//////////////////////////////////////////////////////////////
                          VAULT VALIDATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Validates that an address is a valid Aleph vault
     * @param _vaultFactory The Aleph Vault Factory contract
     * @param _alephVault The vault address to validate
     */
    function validateVault(IAlephVaultFactory _vaultFactory, address _alephVault) internal view {
        if (!_vaultFactory.isValidVault(_alephVault)) {
            revert InvalidAlephVault();
        }
    }

    /*//////////////////////////////////////////////////////////////
                          STRATEGY VALIDATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Validates that a strategy address is not zero
     * @param _strategy The strategy address
     */
    function validateStrategy(address _strategy) internal pure {
        if (_strategy == address(0)) {
            revert InvalidStrategy();
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

interface IAVSRegistrar {
    /**
     * @notice Called by the AllocationManager when an operator wants to register
     * for one or more operator sets. This method should revert if registration
     * is unsuccessful.
     * @param operator the registering operator
     * @param avs the AVS the operator is registering for. This should be the same as IAVSRegistrar.avs()
     * @param operatorSetIds the list of operator set ids being registered for
     * @param data arbitrary data the operator can provide as part of registration
     */
    function registerOperator(
        address operator,
        address avs,
        uint32[] calldata operatorSetIds,
        bytes calldata data
    ) external;

    /**
     * @notice Called by the AllocationManager when an operator is deregistered from one or more operator sets
     * @param operator the deregistering operator
     * @param avs the AVS the operator is deregistering from. This should be the same as IAVSRegistrar.avs()
     * @param operatorSetIds the list of operator set ids being deregistered from
     */
    function deregisterOperator(address operator, address avs, uint32[] calldata operatorSetIds) external;

    /**
     * @notice Returns true if the AVS is supported by the registrar
     * @param avs the AVS to check
     * @return true if the AVS is supported, false otherwise
     */
    function supportsAVS(
        address avs
    ) external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

/**
 * @title Interface for the `PauserRegistry` contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 */
interface IPauserRegistry {
    error OnlyUnpauser();
    error InputAddressZero();

    event PauserStatusChanged(address pauser, bool canPause);

    event UnpauserChanged(address previousUnpauser, address newUnpauser);

    /// @notice Mapping of addresses to whether they hold the pauser role.
    function isPauser(
        address pauser
    ) external view returns (bool);

    /// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
    function unpauser() external view returns (address);
}

File 31 of 81 : ISemVerMixin.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/// @title ISemVerMixin
/// @notice A mixin interface that provides semantic versioning functionality.
/// @dev Follows SemVer 2.0.0 specification (https://semver.org/)
interface ISemVerMixin {
    /// @notice Returns the semantic version string of the contract.
    /// @return The version string in SemVer format (e.g., "1.1.1")
    function version() external view returns (string memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "./ISemVerMixin.sol";

interface ISignatureUtilsMixinErrors {
    /// @notice Thrown when a signature is invalid.
    error InvalidSignature();
    /// @notice Thrown when a signature has expired.
    error SignatureExpired();
}

interface ISignatureUtilsMixinTypes {
    /// @notice Struct that bundles together a signature and an expiration time for the signature.
    /// @dev Used primarily for stack management.
    struct SignatureWithExpiry {
        // the signature itself, formatted as a single bytes object
        bytes signature;
        // the expiration timestamp (UTC) of the signature
        uint256 expiry;
    }

    /// @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature.
    /// @dev Used primarily for stack management.
    struct SignatureWithSaltAndExpiry {
        // the signature itself, formatted as a single bytes object
        bytes signature;
        // the salt used to generate the signature
        bytes32 salt;
        // the expiration timestamp (UTC) of the signature
        uint256 expiry;
    }
}

/**
 * @title The interface for common signature utilities.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 */
interface ISignatureUtilsMixin is ISignatureUtilsMixinErrors, ISignatureUtilsMixinTypes, ISemVerMixin {
    /// @notice Computes the EIP-712 domain separator used for signature validation.
    /// @dev The domain separator is computed according to EIP-712 specification, using:
    ///      - The hardcoded name "EigenLayer"
    ///      - The contract's version string
    ///      - The current chain ID
    ///      - This contract's address
    /// @return The 32-byte domain separator hash used in EIP-712 structured data signing.
    /// @dev See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
    function domainSeparator() external view returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin-upgrades/contracts/utils/math/SafeCastUpgradeable.sol";

/// @dev All scaling factors have `1e18` as an initial/default value. This value is represented
/// by the constant `WAD`, which is used to preserve precision with uint256 math.
///
/// When applying scaling factors, they are typically multiplied/divided by `WAD`, allowing this
/// constant to act as a "1" in mathematical formulae.
uint64 constant WAD = 1e18;

/*
 * There are 2 types of shares:
 *      1. deposit shares
 *          - These can be converted to an amount of tokens given a strategy
 *              - by calling `sharesToUnderlying` on the strategy address (they're already tokens 
 *              in the case of EigenPods)
 *          - These live in the storage of the EigenPodManager and individual StrategyManager strategies 
 *      2. withdrawable shares
 *          - For a staker, this is the amount of shares that they can withdraw
 *          - For an operator, the shares delegated to them are equal to the sum of their stakers'
 *            withdrawable shares
 *
 * Along with a slashing factor, the DepositScalingFactor is used to convert between the two share types.
 */
struct DepositScalingFactor {
    uint256 _scalingFactor;
}

using SlashingLib for DepositScalingFactor global;

library SlashingLib {
    using Math for uint256;
    using SlashingLib for uint256;
    using SafeCastUpgradeable for uint256;

    /// @dev Thrown if an updated deposit scaling factor is 0 to avoid underflow.
    error InvalidDepositScalingFactor();

    // WAD MATH

    function mulWad(uint256 x, uint256 y) internal pure returns (uint256) {
        return x.mulDiv(y, WAD);
    }

    function divWad(uint256 x, uint256 y) internal pure returns (uint256) {
        return x.mulDiv(WAD, y);
    }

    /**
     * @notice Used explicitly for calculating slashed magnitude, we want to ensure even in the
     * situation where an operator is slashed several times and precision has been lost over time,
     * an incoming slashing request isn't rounded down to 0 and an operator is able to avoid slashing penalties.
     */
    function mulWadRoundUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return x.mulDiv(y, WAD, Math.Rounding.Up);
    }

    // GETTERS

    function scalingFactor(
        DepositScalingFactor memory dsf
    ) internal pure returns (uint256) {
        return dsf._scalingFactor == 0 ? WAD : dsf._scalingFactor;
    }

    function scaleForQueueWithdrawal(
        DepositScalingFactor memory dsf,
        uint256 depositSharesToWithdraw
    ) internal pure returns (uint256) {
        return depositSharesToWithdraw.mulWad(dsf.scalingFactor());
    }

    function scaleForCompleteWithdrawal(uint256 scaledShares, uint256 slashingFactor) internal pure returns (uint256) {
        return scaledShares.mulWad(slashingFactor);
    }

    /**
     * @notice Scales shares according to the difference in an operator's magnitude before and
     * after being slashed. This is used to calculate the number of slashable shares in the
     * withdrawal queue.
     * NOTE: max magnitude is guaranteed to only ever decrease.
     */
    function scaleForBurning(
        uint256 scaledShares,
        uint64 prevMaxMagnitude,
        uint64 newMaxMagnitude
    ) internal pure returns (uint256) {
        return scaledShares.mulWad(prevMaxMagnitude - newMaxMagnitude);
    }

    function update(
        DepositScalingFactor storage dsf,
        uint256 prevDepositShares,
        uint256 addedShares,
        uint256 slashingFactor
    ) internal {
        if (prevDepositShares == 0) {
            // If this is the staker's first deposit or they are delegating to an operator,
            // the slashing factor is inverted and applied to the existing DSF. This has the
            // effect of "forgiving" prior slashing for any subsequent deposits.
            dsf._scalingFactor = dsf.scalingFactor().divWad(slashingFactor);
            return;
        }

        /**
         * Base Equations:
         * (1) newShares = currentShares + addedShares
         * (2) newDepositShares = prevDepositShares + addedShares
         * (3) newShares = newDepositShares * newDepositScalingFactor * slashingFactor
         *
         * Plugging (1) into (3):
         * (4) newDepositShares * newDepositScalingFactor * slashingFactor = currentShares + addedShares
         *
         * Solving for newDepositScalingFactor
         * (5) newDepositScalingFactor = (currentShares + addedShares) / (newDepositShares * slashingFactor)
         *
         * Plugging in (2) into (5):
         * (7) newDepositScalingFactor = (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor)
         * Note that magnitudes must be divided by WAD for precision. Thus,
         *
         * (8) newDepositScalingFactor = WAD * (currentShares + addedShares) / ((prevDepositShares + addedShares) * slashingFactor / WAD)
         * (9) newDepositScalingFactor = (currentShares + addedShares) * WAD / (prevDepositShares + addedShares) * WAD / slashingFactor
         */

        // Step 1: Calculate Numerator
        uint256 currentShares = dsf.calcWithdrawable(prevDepositShares, slashingFactor);

        // Step 2: Compute currentShares + addedShares
        uint256 newShares = currentShares + addedShares;

        // Step 3: Calculate newDepositScalingFactor
        /// forgefmt: disable-next-item
        uint256 newDepositScalingFactor = newShares
            .divWad(prevDepositShares + addedShares)
            .divWad(slashingFactor);

        dsf._scalingFactor = newDepositScalingFactor;

        // Avoid potential underflow.
        require(newDepositScalingFactor != 0, InvalidDepositScalingFactor());
    }

    /// @dev Reset the staker's DSF for a strategy by setting it to 0. This is the same
    /// as setting it to WAD (see the `scalingFactor` getter above).
    ///
    /// A DSF is reset when a staker reduces their deposit shares to 0, either by queueing
    /// a withdrawal, or undelegating from their operator. This ensures that subsequent
    /// delegations/deposits do not use a stale DSF (e.g. from a prior operator).
    function reset(
        DepositScalingFactor storage dsf
    ) internal {
        dsf._scalingFactor = 0;
    }

    // CONVERSION

    function calcWithdrawable(
        DepositScalingFactor memory dsf,
        uint256 depositShares,
        uint256 slashingFactor
    ) internal pure returns (uint256) {
        /// forgefmt: disable-next-item
        return depositShares
            .mulWad(dsf.scalingFactor())
            .mulWad(slashingFactor);
    }

    function calcDepositShares(
        DepositScalingFactor memory dsf,
        uint256 withdrawableShares,
        uint256 slashingFactor
    ) internal pure returns (uint256) {
        /// forgefmt: disable-next-item
        return withdrawableShares
            .divWad(dsf.scalingFactor())
            .divWad(slashingFactor);
    }

    function calcSlashedAmount(
        uint256 operatorShares,
        uint256 prevMaxMagnitude,
        uint256 newMaxMagnitude
    ) internal pure returns (uint256) {
        // round up mulDiv so we don't overslash
        return operatorShares - operatorShares.mulDiv(newMaxMagnitude, prevMaxMagnitude, Math.Rounding.Up);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "../libraries/SlashingLib.sol";
import "./IStrategy.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/OperatorSetLib.sol";
/**
 * @title Interface for a `IShareManager` contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice This contract is used by the DelegationManager as a unified interface to interact with the EigenPodManager and StrategyManager
 */

interface IShareManager {
    /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
    /// @dev strategy must be beaconChainETH when talking to the EigenPodManager
    /// @return updatedShares the staker's deposit shares after decrement
    function removeDepositShares(
        address staker,
        IStrategy strategy,
        uint256 depositSharesToRemove
    ) external returns (uint256);

    /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
    /// @dev strategy must be beaconChainETH when talking to the EigenPodManager
    /// @return existingDepositShares the shares the staker had before any were added
    /// @return addedShares the new shares added to the staker's balance
    function addShares(address staker, IStrategy strategy, uint256 shares) external returns (uint256, uint256);

    /// @notice Used by the DelegationManager to convert deposit shares to tokens and send them to a staker
    /// @dev strategy must be beaconChainETH when talking to the EigenPodManager
    /// @dev token is not validated when talking to the EigenPodManager
    function withdrawSharesAsTokens(address staker, IStrategy strategy, IERC20 token, uint256 shares) external;

    /// @notice Returns the current shares of `user` in `strategy`
    /// @dev strategy must be beaconChainETH when talking to the EigenPodManager
    /// @dev returns 0 if the user has negative shares
    function stakerDepositShares(address user, IStrategy strategy) external view returns (uint256 depositShares);

    /**
     * @notice Increase the amount of burnable/redistributable shares for a given Strategy. This is called by the DelegationManager
     * when an operator is slashed in EigenLayer.
     * @param operatorSet The operator set to burn shares in.
     * @param slashId The slash id to burn shares in.
     * @param strategy The strategy to burn shares in.
     * @param addedSharesToBurn The amount of added shares to burn.
     * @dev This function is only called by the DelegationManager when an operator is slashed.
     */
    function increaseBurnOrRedistributableShares(
        OperatorSet calldata operatorSet,
        uint256 slashId,
        IStrategy strategy,
        uint256 addedSharesToBurn
    ) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import "./IETHPOSDeposit.sol";
import "./IStrategyManager.sol";
import "./IEigenPod.sol";
import "./IShareManager.sol";
import "./IPausable.sol";
import "./IStrategy.sol";
import "./ISemVerMixin.sol";

interface IEigenPodManagerErrors {
    /// @dev Thrown when caller is not a EigenPod.
    error OnlyEigenPod();
    /// @dev Thrown when caller is not DelegationManager.
    error OnlyDelegationManager();
    /// @dev Thrown when caller already has an EigenPod.
    error EigenPodAlreadyExists();
    /// @dev Thrown when shares is not a multiple of gwei.
    error SharesNotMultipleOfGwei();
    /// @dev Thrown when shares would result in a negative integer.
    error SharesNegative();
    /// @dev Thrown when the strategy is not the beaconChainETH strategy.
    error InvalidStrategy();
    /// @dev Thrown when the pods shares are negative and a beacon chain balance update is attempted.
    /// The podOwner should complete legacy withdrawal first.
    error LegacyWithdrawalsNotCompleted();
    /// @dev Thrown when caller is not the proof timestamp setter
    error OnlyProofTimestampSetter();
}

interface IEigenPodManagerEvents {
    /// @notice Emitted to notify the deployment of an EigenPod
    event PodDeployed(address indexed eigenPod, address indexed podOwner);

    /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
    event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);

    /// @notice Emitted when the balance of an EigenPod is updated
    event PodSharesUpdated(address indexed podOwner, int256 sharesDelta);

    /// @notice Emitted every time the total shares of a pod are updated
    event NewTotalShares(address indexed podOwner, int256 newTotalShares);

    /// @notice Emitted when a withdrawal of beacon chain ETH is completed
    event BeaconChainETHWithdrawalCompleted(
        address indexed podOwner,
        uint256 shares,
        uint96 nonce,
        address delegatedAddress,
        address withdrawer,
        bytes32 withdrawalRoot
    );

    /// @notice Emitted when a staker's beaconChainSlashingFactor is updated
    event BeaconChainSlashingFactorDecreased(
        address staker, uint64 prevBeaconChainSlashingFactor, uint64 newBeaconChainSlashingFactor
    );

    /// @notice Emitted when an operator is slashed and shares to be burned are increased
    event BurnableETHSharesIncreased(uint256 shares);

    /// @notice Emitted when the Pectra fork timestamp is updated
    event PectraForkTimestampSet(uint64 newPectraForkTimestamp);

    /// @notice Emitted when the proof timestamp setter is updated
    event ProofTimestampSetterSet(address newProofTimestampSetter);
}

interface IEigenPodManagerTypes {
    /**
     * @notice The amount of beacon chain slashing experienced by a pod owner as a proportion of WAD
     * @param isSet whether the slashingFactor has ever been updated. Used to distinguish between
     * a value of "0" and an uninitialized value.
     * @param slashingFactor the proportion of the pod owner's balance that has been decreased due to
     * slashing or other beacon chain balance decreases.
     * @dev NOTE: if !isSet, `slashingFactor` should be treated as WAD. `slashingFactor` is monotonically
     * decreasing and can hit 0 if fully slashed.
     */
    struct BeaconChainSlashingFactor {
        bool isSet;
        uint64 slashingFactor;
    }
}

/**
 * @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 */
interface IEigenPodManager is
    IEigenPodManagerErrors,
    IEigenPodManagerEvents,
    IEigenPodManagerTypes,
    IShareManager,
    IPausable,
    ISemVerMixin
{
    /**
     * @notice Creates an EigenPod for the sender.
     * @dev Function will revert if the `msg.sender` already has an EigenPod.
     * @dev Returns EigenPod address
     */
    function createPod() external returns (address);

    /**
     * @notice Stakes for a new beacon chain validator on the sender's EigenPod.
     * Also creates an EigenPod for the sender if they don't have one already.
     * @param pubkey The 48 bytes public key of the beacon chain validator.
     * @param signature The validator's signature of the deposit data.
     * @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
     */
    function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;

    /**
     * @notice Adds any positive share delta to the pod owner's deposit shares, and delegates them to the pod
     * owner's operator (if applicable). A negative share delta does NOT impact the pod owner's deposit shares,
     * but will reduce their beacon chain slashing factor and delegated shares accordingly.
     * @param podOwner is the pod owner whose balance is being updated.
     * @param prevRestakedBalanceWei is the total amount restaked through the pod before the balance update, including
     * any amount currently in the withdrawal queue.
     * @param balanceDeltaWei is the amount the balance changed
     * @dev Callable only by the podOwner's EigenPod contract.
     * @dev Reverts if `sharesDelta` is not a whole Gwei amount
     */
    function recordBeaconChainETHBalanceUpdate(
        address podOwner,
        uint256 prevRestakedBalanceWei,
        int256 balanceDeltaWei
    ) external;

    /// @notice Sets the address that can set proof timestamps
    function setProofTimestampSetter(
        address newProofTimestampSetter
    ) external;

    /// @notice Sets the Pectra fork timestamp, only callable by `proofTimestampSetter`
    function setPectraForkTimestamp(
        uint64 timestamp
    ) external;

    /// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
    function ownerToPod(
        address podOwner
    ) external view returns (IEigenPod);

    /// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
    function getPod(
        address podOwner
    ) external view returns (IEigenPod);

    /// @notice The ETH2 Deposit Contract
    function ethPOS() external view returns (IETHPOSDeposit);

    /// @notice Beacon proxy to which the EigenPods point
    function eigenPodBeacon() external view returns (IBeacon);

    /// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
    function hasPod(
        address podOwner
    ) external view returns (bool);

    /// @notice Returns the number of EigenPods that have been created
    function numPods() external view returns (uint256);

    /**
     * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
     * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
     * decrease between the pod owner queuing and completing a withdrawal.
     * When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
     * Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
     * as the withdrawal "paying off the deficit".
     */
    function podOwnerDepositShares(
        address podOwner
    ) external view returns (int256);

    /// @notice returns canonical, virtual beaconChainETH strategy
    function beaconChainETHStrategy() external view returns (IStrategy);

    /**
     * @notice Returns the historical sum of proportional balance decreases a pod owner has experienced when
     * updating their pod's balance.
     */
    function beaconChainSlashingFactor(
        address staker
    ) external view returns (uint64);

    /// @notice Returns the accumulated amount of beacon chain ETH Strategy shares
    function burnableETHShares() external view returns (uint256);

    /// @notice Returns the timestamp of the Pectra hard fork
    /// @dev Specifically, this returns the timestamp of the first non-missed slot at or after the Pectra hard fork
    function pectraForkTimestamp() external view returns (uint64);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "./ISemVerMixin.sol";

interface IPermissionControllerErrors {
    /// @notice Thrown when a non-admin caller attempts to perform an admin-only action.
    error NotAdmin();
    /// @notice Thrown when attempting to remove an admin that does not exist.
    error AdminNotSet();
    /// @notice Thrown when attempting to set an appointee for a function that already has one.
    error AppointeeAlreadySet();
    /// @notice Thrown when attempting to interact with a non-existent appointee.
    error AppointeeNotSet();
    /// @notice Thrown when attempting to remove the last remaining admin.
    error CannotHaveZeroAdmins();
    /// @notice Thrown when attempting to set an admin that is already registered.
    error AdminAlreadySet();
    /// @notice Thrown when attempting to interact with an admin that is not in pending status.
    error AdminNotPending();
    /// @notice Thrown when attempting to add an admin that is already pending.
    error AdminAlreadyPending();
}

interface IPermissionControllerEvents {
    /// @notice Emitted when an appointee is set for an account to handle specific function calls.
    event AppointeeSet(address indexed account, address indexed appointee, address target, bytes4 selector);

    /// @notice Emitted when an appointee's permission to handle function calls for an account is revoked.
    event AppointeeRemoved(address indexed account, address indexed appointee, address target, bytes4 selector);

    /// @notice Emitted when an address is set as a pending admin for an account, requiring acceptance.
    event PendingAdminAdded(address indexed account, address admin);

    /// @notice Emitted when a pending admin status is removed for an account before acceptance.
    event PendingAdminRemoved(address indexed account, address admin);

    /// @notice Emitted when an address accepts and becomes an active admin for an account.
    event AdminSet(address indexed account, address admin);

    /// @notice Emitted when an admin's permissions are removed from an account.
    event AdminRemoved(address indexed account, address admin);
}

interface IPermissionController is IPermissionControllerErrors, IPermissionControllerEvents, ISemVerMixin {
    /**
     * @notice Sets a pending admin for an account.
     * @param account The account to set the pending admin for.
     * @param admin The address to set as pending admin.
     * @dev The pending admin must accept the role before becoming an active admin.
     * @dev Multiple admins can be set for a single account.
     */
    function addPendingAdmin(address account, address admin) external;

    /**
     * @notice Removes a pending admin from an account before they have accepted the role.
     * @param account The account to remove the pending admin from.
     * @param admin The pending admin address to remove.
     * @dev Only an existing admin of the account can remove a pending admin.
     */
    function removePendingAdmin(address account, address admin) external;

    /**
     * @notice Allows a pending admin to accept their admin role for an account.
     * @param account The account to accept the admin role for.
     * @dev Only addresses that were previously set as pending admins can accept the role.
     */
    function acceptAdmin(
        address account
    ) external;

    /**
     * @notice Removes an active admin from an account.
     * @param account The account to remove the admin from.
     * @param admin The admin address to remove.
     * @dev Only an existing admin of the account can remove another admin.
     * @dev Will revert if removing this admin would leave the account with zero admins.
     */
    function removeAdmin(address account, address admin) external;

    /**
     * @notice Sets an appointee who can call specific functions on behalf of an account.
     * @param account The account to set the appointee for.
     * @param appointee The address to be given permission.
     * @param target The contract address the appointee can interact with.
     * @param selector The function selector the appointee can call.
     * @dev Only an admin of the account can set appointees.
     */
    function setAppointee(address account, address appointee, address target, bytes4 selector) external;

    /**
     * @notice Removes an appointee's permission to call a specific function.
     * @param account The account to remove the appointee from.
     * @param appointee The appointee address to remove.
     * @param target The contract address to remove permissions for.
     * @param selector The function selector to remove permissions for.
     * @dev Only an admin of the account can remove appointees.
     */
    function removeAppointee(address account, address appointee, address target, bytes4 selector) external;

    /**
     * @notice Checks if a given address is an admin of an account.
     * @param account The account to check admin status for.
     * @param caller The address to check.
     * @dev If the account has no admins, returns true only if the caller is the account itself.
     * @return Returns true if the caller is an admin, false otherwise.
     */
    function isAdmin(address account, address caller) external view returns (bool);

    /**
     * @notice Checks if an address is currently a pending admin for an account.
     * @param account The account to check pending admin status for.
     * @param pendingAdmin The address to check.
     * @return Returns true if the address is a pending admin, false otherwise.
     */
    function isPendingAdmin(address account, address pendingAdmin) external view returns (bool);

    /**
     * @notice Retrieves all active admins for an account.
     * @param account The account to get the admins for.
     * @dev If the account has no admins, returns an array containing only the account address.
     * @return An array of admin addresses.
     */
    function getAdmins(
        address account
    ) external view returns (address[] memory);

    /**
     * @notice Retrieves all pending admins for an account.
     * @param account The account to get the pending admins for.
     * @return An array of pending admin addresses.
     */
    function getPendingAdmins(
        address account
    ) external view returns (address[] memory);

    /**
     * @notice Checks if a caller has permission to call a specific function.
     * @param account The account to check permissions for.
     * @param caller The address attempting to make the call.
     * @param target The contract address being called.
     * @param selector The function selector being called.
     * @dev Returns true if the caller is either an admin or an appointed caller.
     * @dev Be mindful that upgrades to the contract may invalidate the appointee's permissions.
     * This is only possible if a function's selector changes (e.g. if a function's parameters are modified).
     * @return Returns true if the caller has permission, false otherwise.
     */
    function canCall(address account, address caller, address target, bytes4 selector) external returns (bool);

    /**
     * @notice Retrieves all permissions granted to an appointee for a given account.
     * @param account The account to check appointee permissions for.
     * @param appointee The appointee address to check.
     * @return Two arrays: target contract addresses and their corresponding function selectors.
     */
    function getAppointeePermissions(
        address account,
        address appointee
    ) external returns (address[] memory, bytes4[] memory);

    /**
     * @notice Retrieves all appointees that can call a specific function for an account.
     * @param account The account to get appointees for.
     * @param target The contract address to check.
     * @param selector The function selector to check.
     * @dev Does not include admins in the returned list, even though they have calling permission.
     * @return An array of appointee addresses.
     */
    function getAppointees(address account, address target, bytes4 selector) external returns (address[] memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)

pragma solidity >=0.6.2;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(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);
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
/*
  ______   __                      __
 /      \ /  |                    /  |
/$$$$$$  |$$ |  ______    ______  $$ |____
$$ |__$$ |$$ | /      \  /      \ $$      \
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/
                        $$ |
                        $$ |
                        $$/
*/

import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol";

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
library AuthLibrary {
    using ECDSA for bytes32;
    using MessageHashUtils for bytes32;

    /**
     * @notice The flow for the settle deposit.
     */
    bytes4 internal constant SETTLE_DEPOSIT = bytes4(keccak256("SETTLE_DEPOSIT"));
    /**
     * @notice The flow for the settle redeem.
     */
    bytes4 internal constant SETTLE_REDEEM = bytes4(keccak256("SETTLE_REDEEM"));

    /*//////////////////////////////////////////////////////////////
                            ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice The error thrown when the auth signature is expired.
     */
    error AuthSignatureExpired();
    /**
     * @notice The error thrown when the auth signature is invalid.
     */
    error InvalidAuthSignature();

    /*//////////////////////////////////////////////////////////////
                            STRUCTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice The auth signature.
     * @param authSignature The auth signature.
     * @param expiryBlock The expiry block.
     */
    struct AuthSignature {
        bytes authSignature;
        uint256 expiryBlock;
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Verifies the vault deployment auth signature.
     * @param _vaultFactory The vault factory.
     * @param _name The name of the vault.
     * @param _configId The config ID of the vault.
     * @param _authSigner The auth signer.
     * @param _authSignature The auth signature.
     */
    function verifyVaultDeploymentAuthSignature(
        address _vaultFactory,
        string memory _name,
        string memory _configId,
        address _authSigner,
        AuthSignature memory _authSignature
    ) internal view {
        bytes32 _hash = keccak256(
            abi.encode(msg.sender, _vaultFactory, _name, _configId, block.chainid, _authSignature.expiryBlock)
        );
        _verifyAuthSignature(_hash, _authSigner, _authSignature);
    }

    /**
     * @notice Verifies the deposit request auth signature.
     * @param _classId The class ID of the deposit request.
     * @param _authSigner The auth signer.
     * @param _authSignature The auth signature.
     */
    function verifyDepositRequestAuthSignature(uint8 _classId, address _authSigner, AuthSignature memory _authSignature)
        internal
        view
    {
        bytes32 _hash =
            keccak256(abi.encode(msg.sender, address(this), block.chainid, _classId, _authSignature.expiryBlock));
        _verifyAuthSignature(_hash, _authSigner, _authSignature);
    }

    /**
     * @notice Verifies the settlement auth signature.
     * @param _flow The flow of the settlement.
     * @param _classId The class ID of the settlement.
     * @param _toBatchId The batch ID of the settlement.
     * @param _manager The manager of the settlement.
     * @param _newTotalAssets The new total assets of the settlement.
     * @param _authSigner The auth signer.
     * @param _authSignature The auth signature.
     */
    function verifySettlementAuthSignature(
        bytes4 _flow,
        uint8 _classId,
        uint48 _toBatchId,
        address _manager,
        uint256[] calldata _newTotalAssets,
        address _authSigner,
        AuthSignature memory _authSignature
    ) internal view {
        bytes32 _hash = keccak256(
            abi.encode(
                _flow,
                _manager,
                address(this),
                block.chainid,
                _classId,
                _toBatchId,
                _newTotalAssets,
                _authSignature.expiryBlock
            )
        );
        _verifyAuthSignature(_hash, _authSigner, _authSignature);
    }

    /**
     * @notice Verifies the auth signature.
     * @param _hash The hash of the auth signature.
     * @param _authSigner The auth signer.
     * @param _authSignature The auth signature.
     */
    function _verifyAuthSignature(bytes32 _hash, address _authSigner, AuthSignature memory _authSignature)
        internal
        view
    {
        if (_authSignature.expiryBlock < block.number) {
            revert AuthSignatureExpired();
        }
        address _signer = _hash.toEthSignedMessageHash().recover(_authSignature.authSignature);
        if (_signer != _authSigner) {
            revert InvalidAuthSignature();
        }
    }
}

File 42 of 81 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

import {Arrays} from "../Arrays.sol";
import {Math} from "../math/Math.sol";

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 * - Set can be cleared (all elements removed) in O(n).
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * The following types are supported:
 *
 * - `bytes32` (`Bytes32Set`) since v3.3.0
 * - `address` (`AddressSet`) since v3.3.0
 * - `uint256` (`UintSet`) since v3.3.0
 * - `string` (`StringSet`) since v5.4.0
 * - `bytes` (`BytesSet`) since v5.4.0
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
     * using it may render the function uncallable if the set grows to the point where clearing it consumes too much
     * gas to fit in a block.
     */
    function _clear(Set storage set) private {
        uint256 len = _length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
        unchecked {
            end = Math.min(end, _length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            bytes32[] memory result = new bytes32[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(Bytes32Set storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(AddressSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(UintSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    struct StringSet {
        // Storage of set values
        string[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(string value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(StringSet storage set, string memory value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(StringSet storage set, string memory value) internal returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                string memory lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(StringSet storage set) internal {
        uint256 len = length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(StringSet storage set, string memory value) internal view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(StringSet storage set) internal view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(StringSet storage set, uint256 index) internal view returns (string memory) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(StringSet storage set) internal view returns (string[] memory) {
        return set._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
        unchecked {
            end = Math.min(end, length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            string[] memory result = new string[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }

    struct BytesSet {
        // Storage of set values
        bytes[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(BytesSet storage set, bytes memory value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(BytesSet storage set, bytes memory value) internal returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes memory lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(BytesSet storage set) internal {
        uint256 len = length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(BytesSet storage set) internal view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(BytesSet storage set) internal view returns (bytes[] memory) {
        return set._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
        unchecked {
            end = Math.min(end, length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            bytes[] memory result = new bytes[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";

/**
 * @title ERC20Token
 * @notice Mintable/burnable ERC20 token for representing slashed vault shares
 * @dev On Sepolia testnet, anyone can mint and burn tokens for testing purposes.
 *      On all other chains (including mainnet), only the owner (AlephAVS) can mint and burn tokens.
 */
contract ERC20Token is ERC20, Ownable {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when tokens are minted
     * @param to The address receiving the minted tokens
     * @param amount The amount of tokens minted
     */
    event SlashedTokenMinted(address indexed to, uint256 amount);

    /**
     * @notice Emitted when tokens are burned
     * @param from The address whose tokens are burned
     * @param amount The amount of tokens burned
     */
    event SlashedTokenBurned(address indexed from, uint256 amount);

    uint8 private immutable DECIMALS;

    /// @dev Mainnet chain ID (1)
    uint256 private constant MAINNET_CHAIN_ID = 1;
    /// @dev Sepolia testnet chain ID (11155111)
    uint256 private constant SEPOLIA_CHAIN_ID = 11155111;

    /**
     * @notice Constructs the ERC20Token contract
     * @param _name The name of the token
     * @param _symbol The symbol of the token
     * @param _decimals The number of decimals for the token
     * @param _owner The owner of the contract
     */
    constructor(string memory _name, string memory _symbol, uint8 _decimals, address _owner)
        ERC20(_name, _symbol)
        Ownable(_owner)
    {
        DECIMALS = _decimals;
    }

    /**
     * @notice Returns the number of decimals for the token
     * @return The number of decimals
     */
    function decimals() public view virtual override returns (uint8) {
        return DECIMALS;
    }

    /**
     * @notice Mints new tokens to the specified address
     * @param to The address to receive the tokens
     * @param amount The amount of tokens to mint
     * @dev On Sepolia testnet, anyone can mint. On other chains, only the owner can mint.
     */
    function mint(address to, uint256 amount) external {
        // On Sepolia testnet, allow anyone to mint. On other chains, require owner.
        if (block.chainid == SEPOLIA_CHAIN_ID) {
            // Sepolia testnet - allow anyone, no owner check needed
        } else {
            _checkOwner();
        }
        _mint(to, amount);
        emit SlashedTokenMinted(to, amount);
    }

    /**
     * @notice Burns tokens from the specified address
     * @param from The address to burn tokens from
     * @param amount The amount of tokens to burn
     * @dev On Sepolia testnet, anyone can burn. On other chains, only the owner can burn.
     */
    function burn(address from, uint256 amount) external {
        // On Sepolia testnet, allow anyone to burn. On other chains, require owner.
        if (block.chainid == SEPOLIA_CHAIN_ID) {
            // Sepolia testnet - allow anyone, no owner check needed
        } else {
            _checkOwner();
        }
        _burn(from, amount);
        emit SlashedTokenBurned(from, amount);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
interface IAlephAVSPausable {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when a flow is paused
     * @param _pausableFlow The flow identifier
     * @param _pauser The address that paused the flow
     */
    event FlowPaused(bytes4 _pausableFlow, address _pauser);

    /**
     * @notice Emitted when a flow is unpaused
     * @param _pausableFlow The flow identifier
     * @param _unpauser The address that unpaused the flow
     */
    event FlowUnpaused(bytes4 _pausableFlow, address _unpauser);

    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Emitted when a flow is currently paused
     */
    error FlowIsCurrentlyPaused();

    /**
     * @notice Emitted when a flow is currently unpaused
     */
    error FlowIsCurrentlyUnpaused();

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Checks if a specific flow is currently paused
     * @param _pausableFlow The flow identifier to check
     * @return True if the flow is paused, false otherwise
     */
    function isFlowPaused(bytes4 _pausableFlow) external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                            PAUSING FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Pauses a specific flow to prevent its execution
     * @param _pausableFlow The flow identifier to pause
     * @dev Only callable by users with the flow-specific role. Reverts if flow is already paused.
     */
    function pause(bytes4 _pausableFlow) external;

    /**
     * @notice Unpauses a specific flow to allow its execution
     * @param _pausableFlow The flow identifier to unpause
     * @dev Only callable by users with the flow-specific role. Reverts if flow is not paused.
     */
    function unpause(bytes4 _pausableFlow) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(account),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }

    /**
     * @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 47 of 81 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Return the 512-bit addition of two uint256.
     *
     * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
     */
    function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        assembly ("memory-safe") {
            low := add(a, b)
            high := lt(low, a)
        }
    }

    /**
     * @dev Return the 512-bit multiplication of two uint256.
     *
     * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
     */
    function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
        // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
        // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = high * 2²⁵⁶ + low.
        assembly ("memory-safe") {
            let mm := mulmod(a, b, not(0))
            low := mul(a, b)
            high := sub(sub(mm, low), lt(mm, low))
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            success = c >= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a - b;
            success = c <= a;
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a * b;
            assembly ("memory-safe") {
                // Only true when the multiplication doesn't overflow
                // (c / a == b) || (a == 0)
                success := or(eq(div(c, a), b), iszero(a))
            }
            // equivalent to: success ? c : 0
            result = c * SafeCast.toUint(success);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `DIV` opcode returns zero when the denominator is 0.
                result := div(a, b)
            }
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            success = b > 0;
            assembly ("memory-safe") {
                // The `MOD` opcode returns zero when the denominator is 0.
                result := mod(a, b)
            }
        }
    }

    /**
     * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryAdd(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
     */
    function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
        (, uint256 result) = trySub(a, b);
        return result;
    }

    /**
     * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
     */
    function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
        (bool success, uint256 result) = tryMul(a, b);
        return ternary(success, result, type(uint256).max);
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(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 towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * 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 {
            (uint256 high, uint256 low) = mul512(x, y);

            // Handle non-overflow cases, 256 by 256 division.
            if (high == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return low / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= high) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [high low].
            uint256 remainder;
            assembly ("memory-safe") {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                high := sub(high, gt(remainder, low))
                low := sub(low, 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.

            uint256 twos = denominator & (0 - denominator);
            assembly ("memory-safe") {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [high low] by twos.
                low := div(low, twos)

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from high into low.
            low |= high * twos;

            // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            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⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // 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²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
            // is no longer required.
            result = low * inverse;
            return result;
        }
    }

    /**
     * @dev 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) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
     */
    function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
        unchecked {
            (uint256 high, uint256 low) = mul512(x, y);
            if (high >= 1 << n) {
                Panic.panic(Panic.UNDER_OVERFLOW);
            }
            return (high << (256 - n)) | (low >> n);
        }
    }

    /**
     * @dev Calculates x * y >> n with full precision, following the selected rounding direction.
     */
    function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
        return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // If upper 8 bits of 16-bit half set, add 8 to result
        r |= SafeCast.toUint((x >> r) > 0xff) << 3;
        // If upper 4 bits of 8-bit half set, add 4 to result
        r |= SafeCast.toUint((x >> r) > 0xf) << 2;

        // Shifts value right by the current result and use it as an index into this lookup table:
        //
        // | x (4 bits) |  index  | table[index] = MSB position |
        // |------------|---------|-----------------------------|
        // |    0000    |    0    |        table[0] = 0         |
        // |    0001    |    1    |        table[1] = 0         |
        // |    0010    |    2    |        table[2] = 1         |
        // |    0011    |    3    |        table[3] = 1         |
        // |    0100    |    4    |        table[4] = 2         |
        // |    0101    |    5    |        table[5] = 2         |
        // |    0110    |    6    |        table[6] = 2         |
        // |    0111    |    7    |        table[7] = 2         |
        // |    1000    |    8    |        table[8] = 3         |
        // |    1001    |    9    |        table[9] = 3         |
        // |    1010    |   10    |        table[10] = 3        |
        // |    1011    |   11    |        table[11] = 3        |
        // |    1100    |   12    |        table[12] = 3        |
        // |    1101    |   13    |        table[13] = 3        |
        // |    1110    |   14    |        table[14] = 3        |
        // |    1111    |   15    |        table[15] = 3        |
        //
        // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
        assembly ("memory-safe") {
            r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
        }
    }

    /**
     * @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * 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 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
        return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
    }

    /**
     * @dev Return the log in base 256, 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }

    /**
     * @dev Counts the number of leading zero bits in a uint256.
     */
    function clz(uint256 x) internal pure returns (uint256) {
        return ternary(x == 0, 256, 255 - log2(x));
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.30;

import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";

/**
 * @title AlephUtils
 * @notice Utility library for common functions used across Aleph contracts
 * @dev This library provides reusable utility functions to reduce code duplication
 */
library AlephUtils {
    /*//////////////////////////////////////////////////////////////
                                 CONSTANTS
    //////////////////////////////////////////////////////////////*/

    uint32 public constant LST_STRATEGIES_OPERATOR_SET_ID = 0;
    uint32 public constant SLASHED_STRATEGIES_OPERATOR_SET_ID = 1;
    uint64 public constant OPERATOR_SET_MAGNITUDE = 1e18;
    uint256 public constant WAD = 1e18;
    uint96 public constant REWARD_MULTIPLIER = uint96(1e18);

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error InvalidAddress();

    /*//////////////////////////////////////////////////////////////
                          VALIDATION FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Validates that an address is not the zero address
     * @param _address The address to validate
     * @dev Reverts with InvalidAddress if the address is zero
     */
    function validateAddress(address _address) internal pure {
        if (_address == address(0)) {
            revert InvalidAddress();
        }
    }

    /**
     * @notice Validates that an address is not the zero address with custom error selector
     * @param _address The address to validate
     * @param _errorSelector The error selector to use if validation fails
     * @dev Uses assembly to revert with the provided error selector
     */
    function validateAddressWithSelector(address _address, bytes4 _errorSelector) internal pure {
        if (_address == address(0)) {
            assembly {
                mstore(0x00, _errorSelector)
                revert(0x00, 0x04)
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                          STRATEGY FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Converts a single strategy to an array
     * @param _strategy The strategy to convert
     * @return An array containing the single strategy
     */
    function asStrategyArray(IStrategy _strategy) internal pure returns (IStrategy[] memory) {
        IStrategy[] memory arr = new IStrategy[](1);
        arr[0] = _strategy;
        return arr;
    }

    /**
     * @notice Creates a single-element strategy array with magnitude
     * @param _strategy The strategy
     * @param _magnitude The magnitude
     * @return strategies Array of strategies
     * @return magnitudes Array of magnitudes
     */
    function createStrategyAllocationParams(IStrategy _strategy, uint64 _magnitude)
        internal
        pure
        returns (IStrategy[] memory strategies, uint64[] memory magnitudes)
    {
        strategies = new IStrategy[](1);
        strategies[0] = _strategy;
        magnitudes = new uint64[](1);
        magnitudes[0] = _magnitude;
    }

    /*//////////////////////////////////////////////////////////////
                          OPERATOR SET FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Gets an operator set for a given AVS address
     * @param _avsAddress The AVS contract address
     * @param _operatorSetId The operator set ID
     * @return The operator set
     */
    function getOperatorSet(address _avsAddress, uint32 _operatorSetId) internal pure returns (OperatorSet memory) {
        return OperatorSet(_avsAddress, _operatorSetId);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity >=0.6.2;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @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);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";

/**
 * @dev This library adds helper functions for ERC4626 math operations.
 */
library ERC4626Math {
    using Math for uint256;

    /**
     * @notice The total number of share units alloted to any user in a given class.
     */
    uint256 public constant TOTAL_SHARE_UNITS = 1e18;

    function previewDeposit(uint256 assets, uint256 totalShares, uint256 totalAssets) internal pure returns (uint256) {
        return convertToShares(assets, totalShares, totalAssets, Math.Rounding.Floor);
    }

    function previewMint(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return convertToAssets(shares, totalAssets, totalShares, Math.Rounding.Ceil);
    }

    function previewWithdraw(uint256 assets, uint256 totalShares, uint256 totalAssets) internal pure returns (uint256) {
        return convertToShares(assets, totalShares, totalAssets, Math.Rounding.Ceil);
    }

    function previewRedeem(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return convertToAssets(shares, totalAssets, totalShares, Math.Rounding.Floor);
    }

    /**
     * @notice Preview the amount of assets to redeem based on the number of share units.
     * @param shareUnits The number of share units to redeem.
     * @param totalAssets The total amount of assets.
     * @return The amount of assets to redeem.
     */
    function previewMintUnits(uint256 shareUnits, uint256 totalAssets) internal pure returns (uint256) {
        return convertToAssets(shareUnits, totalAssets);
    }

    /**
     * @notice Preview the number of share units to redeem based on the amount of assets.
     * @param assets The amount of assets to withdraw.
     * @param totalAssets The total amount of assets.
     * @return The number of share units to redeem.
     */
    function previewWithdrawUnits(uint256 assets, uint256 totalAssets) internal pure returns (uint256) {
        return convertToShareUnits(assets, totalAssets);
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function convertToShares(uint256 assets, uint256 totalShares, uint256 totalAssets, Math.Rounding rounding)
        internal
        pure
        returns (uint256)
    {
        return assets.mulDiv(totalShares + 10 ** _decimalsOffset(), totalAssets + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function convertToAssets(uint256 shares, uint256 totalAssets, uint256 totalShares, Math.Rounding rounding)
        internal
        pure
        returns (uint256)
    {
        return shares.mulDiv(totalAssets + 1, totalShares + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Internal conversion function (from assets to share units) with support for rounding direction.
     */
    function convertToShareUnits(uint256 assets, uint256 totalAssets) internal pure returns (uint256) {
        return assets.mulDiv(TOTAL_SHARE_UNITS, totalAssets, Math.Rounding.Floor);
    }

    /**
     * @dev Internal conversion function (from share units to assets) with support for rounding direction.
     */
    function convertToAssets(uint256 shareUnits, uint256 totalAssets) internal pure returns (uint256) {
        return shareUnits.mulDiv(totalAssets, TOTAL_SHARE_UNITS, Math.Rounding.Ceil);
    }

    function _decimalsOffset() private pure returns (uint8) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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 256, 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 << 3) < value ? 1 : 0);
        }
    }
}

File 52 of 81 : SafeCastUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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);
    }
}

// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// SPDX-License-Identifier: CC0-1.0

pragma solidity >=0.5.0;

// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IETHPOSDeposit {
    /// @notice A processed deposit event.
    event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);

    /// @notice Submit a Phase 0 DepositData object.
    /// @param pubkey A BLS12-381 public key.
    /// @param withdrawal_credentials Commitment to a public key for withdrawals.
    /// @param signature A BLS12-381 signature.
    /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
    /// Used as a protection against malformed input.
    function deposit(
        bytes calldata pubkey,
        bytes calldata withdrawal_credentials,
        bytes calldata signature,
        bytes32 deposit_data_root
    ) external payable;

    /// @notice Query the current deposit root hash.
    /// @return The deposit root hash.
    function get_deposit_root() external view returns (bytes32);

    /// @notice Query the current deposit count.
    /// @return The deposit count encoded as a little endian 64-bit number.
    function get_deposit_count() external view returns (bytes memory);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

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

import "../libraries/BeaconChainProofs.sol";
import "./ISemVerMixin.sol";
import "./IEigenPodManager.sol";

interface IEigenPodErrors {
    /// @dev Thrown when msg.sender is not the EPM.
    error OnlyEigenPodManager();
    /// @dev Thrown when msg.sender is not the pod owner.
    error OnlyEigenPodOwner();
    /// @dev Thrown when msg.sender is not owner or the proof submitter.
    error OnlyEigenPodOwnerOrProofSubmitter();
    /// @dev Thrown when attempting an action that is currently paused.
    error CurrentlyPaused();

    /// Invalid Inputs

    /// @dev Thrown when an address of zero is provided.
    error InputAddressZero();
    /// @dev Thrown when two array parameters have mismatching lengths.
    error InputArrayLengthMismatch();
    /// @dev Thrown when `validatorPubKey` length is not equal to 48-bytes.
    error InvalidPubKeyLength();
    /// @dev Thrown when provided timestamp is out of range.
    error TimestampOutOfRange();

    /// Checkpoints

    /// @dev Thrown when no active checkpoints are found.
    error NoActiveCheckpoint();
    /// @dev Thrown if an uncompleted checkpoint exists.
    error CheckpointAlreadyActive();
    /// @dev Thrown if there's not a balance available to checkpoint.
    error NoBalanceToCheckpoint();
    /// @dev Thrown when attempting to create a checkpoint twice within a given block.
    error CannotCheckpointTwiceInSingleBlock();

    /// Withdrawing

    /// @dev Thrown when amount exceeds `restakedExecutionLayerGwei`.
    error InsufficientWithdrawableBalance();

    /// Validator Status

    /// @dev Thrown when a validator's withdrawal credentials have already been verified.
    error CredentialsAlreadyVerified();
    /// @dev Thrown if the provided proof is not valid for this EigenPod.
    error WithdrawalCredentialsNotForEigenPod();
    /// @dev Thrown when a validator is not in the ACTIVE status in the pod.
    error ValidatorNotActiveInPod();
    /// @dev Thrown when validator is not active yet on the beacon chain.
    error ValidatorInactiveOnBeaconChain();
    /// @dev Thrown if a validator is exiting the beacon chain.
    error ValidatorIsExitingBeaconChain();
    /// @dev Thrown when a validator has not been slashed on the beacon chain.
    error ValidatorNotSlashedOnBeaconChain();

    /// Consolidation and Withdrawal Requests

    /// @dev Thrown when a predeploy request is initiated with insufficient msg.value
    error InsufficientFunds();
    /// @dev Thrown when calling the predeploy fails
    error PredeployFailed();
    /// @dev Thrown when querying a predeploy for its current fee fails
    error FeeQueryFailed();

    /// Misc

    /// @dev Thrown when an invalid block root is returned by the EIP-4788 oracle.
    error InvalidEIP4788Response();
    /// @dev Thrown when attempting to send an invalid amount to the beacon deposit contract.
    error MsgValueNot32ETH();
    /// @dev Thrown when provided `beaconTimestamp` is too far in the past.
    error BeaconTimestampTooFarInPast();
    /// @dev Thrown when provided `beaconTimestamp` is before the last checkpoint
    error BeaconTimestampBeforeLatestCheckpoint();
    /// @dev Thrown when the pectraForkTimestamp returned from the EigenPodManager is zero
    error ForkTimestampZero();
}

interface IEigenPodTypes {
    enum VALIDATOR_STATUS {
        INACTIVE, // doesnt exist
        ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
        WITHDRAWN // withdrawn from the Beacon Chain

    }

    /**
     * @param validatorIndex index of the validator on the beacon chain
     * @param restakedBalanceGwei amount of beacon chain ETH restaked on EigenLayer in gwei
     * @param lastCheckpointedAt timestamp of the validator's most recent balance update
     * @param status last recorded status of the validator
     */
    struct ValidatorInfo {
        uint64 validatorIndex;
        uint64 restakedBalanceGwei;
        uint64 lastCheckpointedAt;
        VALIDATOR_STATUS status;
    }

    struct Checkpoint {
        bytes32 beaconBlockRoot;
        uint24 proofsRemaining;
        uint64 podBalanceGwei;
        int64 balanceDeltasGwei;
        uint64 prevBeaconBalanceGwei;
    }

    /**
     * @param srcPubkey the pubkey of the source validator for the consolidation
     * @param targetPubkey the pubkey of the target validator for the consolidation
     * @dev Note that if srcPubkey == targetPubkey, this is a "switch request," and will
     * change the validator's withdrawal credential type from 0x01 to 0x02.
     * For more notes on usage, see `requestConsolidation`
     */
    struct ConsolidationRequest {
        bytes srcPubkey;
        bytes targetPubkey;
    }

    /**
     * @param pubkey the pubkey of the validator to withdraw from
     * @param amountGwei the amount (in gwei) to withdraw from the beacon chain to the pod
     * @dev Note that if amountGwei == 0, this is a "full exit request," and will fully exit
     * the validator to the pod.
     * For more notes on usage, see `requestWithdrawal`
     */
    struct WithdrawalRequest {
        bytes pubkey;
        uint64 amountGwei;
    }
}

interface IEigenPodEvents is IEigenPodTypes {
    /// @notice Emitted when an ETH validator stakes via this eigenPod
    event EigenPodStaked(bytes32 pubkeyHash);

    /// @notice Emitted when a pod owner updates the proof submitter address
    event ProofSubmitterUpdated(address prevProofSubmitter, address newProofSubmitter);

    /// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
    event ValidatorRestaked(bytes32 pubkeyHash);

    /// @notice Emitted when an ETH validator's  balance is proven to be updated.  Here newValidatorBalanceGwei
    //  is the validator's balance that is credited on EigenLayer.
    event ValidatorBalanceUpdated(bytes32 pubkeyHash, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei);

    /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
    event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);

    /// @notice Emitted when ETH is received via the `receive` fallback
    event NonBeaconChainETHReceived(uint256 amountReceived);

    /// @notice Emitted when a checkpoint is created
    event CheckpointCreated(
        uint64 indexed checkpointTimestamp, bytes32 indexed beaconBlockRoot, uint256 validatorCount
    );

    /// @notice Emitted when a checkpoint is finalized
    event CheckpointFinalized(uint64 indexed checkpointTimestamp, int256 totalShareDeltaWei);

    /// @notice Emitted when a validator is proven for a given checkpoint
    event ValidatorCheckpointed(uint64 indexed checkpointTimestamp, bytes32 indexed pubkeyHash);

    /// @notice Emitted when a validator is proven to have 0 balance at a given checkpoint
    event ValidatorWithdrawn(uint64 indexed checkpointTimestamp, bytes32 indexed pubkeyHash);

    /// @notice Emitted when a consolidation request is initiated where source == target
    event SwitchToCompoundingRequested(bytes32 indexed validatorPubkeyHash);

    /// @notice Emitted when a standard consolidation request is initiated
    event ConsolidationRequested(bytes32 indexed sourcePubkeyHash, bytes32 indexed targetPubkeyHash);

    /// @notice Emitted when a withdrawal request is initiated where request.amountGwei == 0
    event ExitRequested(bytes32 indexed validatorPubkeyHash);

    /// @notice Emitted when a partial withdrawal request is initiated
    event WithdrawalRequested(bytes32 indexed validatorPubkeyHash, uint64 withdrawalAmountGwei);
}

/**
 * @title The implementation contract used for restaking beacon chain ETH on EigenLayer
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @dev Note that all beacon chain balances are stored as gwei within the beacon chain datastructures. We choose
 *   to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts
 */
interface IEigenPod is IEigenPodErrors, IEigenPodEvents, ISemVerMixin {
    /// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
    function initialize(
        address owner
    ) external;

    /// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
    /// @dev This function only supports staking to a 0x01 validator. For compounding validators, please interact directly with the deposit contract.
    function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;

    /**
     * @notice Transfers `amountWei` from this contract to the `recipient`. Only callable by the EigenPodManager as part
     * of the DelegationManager's withdrawal flow.
     * @dev `amountWei` is not required to be a whole Gwei amount. Amounts less than a Gwei multiple may be unrecoverable due to Gwei conversion.
     */
    function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;

    /**
     * @dev Create a checkpoint used to prove this pod's active validator set. Checkpoints are completed
     * by submitting one checkpoint proof per ACTIVE validator. During the checkpoint process, the total
     * change in ACTIVE validator balance is tracked, and any validators with 0 balance are marked `WITHDRAWN`.
     * @dev Once finalized, the pod owner is awarded shares corresponding to:
     * - the total change in their ACTIVE validator balances
     * - any ETH in the pod not already awarded shares
     * @dev A checkpoint cannot be created if the pod already has an outstanding checkpoint. If
     * this is the case, the pod owner MUST complete the existing checkpoint before starting a new one.
     * @param revertIfNoBalance Forces a revert if the pod ETH balance is 0. This allows the pod owner
     * to prevent accidentally starting a checkpoint that will not increase their shares
     */
    function startCheckpoint(
        bool revertIfNoBalance
    ) external;

    /**
     * @dev Progress the current checkpoint towards completion by submitting one or more validator
     * checkpoint proofs. Anyone can call this method to submit proofs towards the current checkpoint.
     * For each validator proven, the current checkpoint's `proofsRemaining` decreases.
     * @dev If the checkpoint's `proofsRemaining` reaches 0, the checkpoint is finalized.
     * (see `_updateCheckpoint` for more details)
     * @dev This method can only be called when there is a currently-active checkpoint.
     * @param balanceContainerProof proves the beacon's current balance container root against a checkpoint's `beaconBlockRoot`
     * @param proofs Proofs for one or more validator current balances against the `balanceContainerRoot`
     */
    function verifyCheckpointProofs(
        BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof,
        BeaconChainProofs.BalanceProof[] calldata proofs
    ) external;

    /**
     * @dev Verify one or more validators have their withdrawal credentials pointed at this EigenPod, and award
     * shares based on their effective balance. Proven validators are marked `ACTIVE` within the EigenPod, and
     * future checkpoint proofs will need to include them.
     * @dev Withdrawal credential proofs MUST NOT be older than `currentCheckpointTimestamp`.
     * @dev Validators proven via this method MUST NOT have an exit epoch set already.
     * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds
     * to the parent beacon block root against which the proof is verified.
     * @param stateRootProof proves a beacon state root against a beacon block root
     * @param validatorIndices a list of validator indices being proven
     * @param validatorFieldsProofs proofs of each validator's `validatorFields` against the beacon state root
     * @param validatorFields the fields of the beacon chain "Validator" container. See consensus specs for
     * details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
     */
    function verifyWithdrawalCredentials(
        uint64 beaconTimestamp,
        BeaconChainProofs.StateRootProof calldata stateRootProof,
        uint40[] calldata validatorIndices,
        bytes[] calldata validatorFieldsProofs,
        bytes32[][] calldata validatorFields
    ) external;

    /**
     * @dev Prove that one of this pod's active validators was slashed on the beacon chain. A successful
     * staleness proof allows the caller to start a checkpoint.
     *
     * @dev Note that in order to start a checkpoint, any existing checkpoint must already be completed!
     * (See `_startCheckpoint` for details)
     *
     * @dev Note that this method allows anyone to start a checkpoint as soon as a slashing occurs on the beacon
     * chain. This is intended to make it easier to external watchers to keep a pod's balance up to date.
     *
     * @dev Note too that beacon chain slashings are not instant. There is a delay between the initial slashing event
     * and the validator's final exit back to the execution layer. During this time, the validator's balance may or
     * may not drop further due to a correlation penalty. This method allows proof of a slashed validator
     * to initiate a checkpoint for as long as the validator remains on the beacon chain. Once the validator
     * has exited and been checkpointed at 0 balance, they are no longer "checkpoint-able" and cannot be proven
     * "stale" via this method.
     * See https://eth2book.info/capella/part3/transition/epoch/#slashings for more info.
     *
     * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds
     * to the parent beacon block root against which the proof is verified.
     * @param stateRootProof proves a beacon state root against a beacon block root
     * @param proof the fields of the beacon chain "Validator" container, along with a merkle proof against
     * the beacon state root. See the consensus specs for more details:
     * https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
     *
     * @dev Staleness conditions:
     * - Validator's last checkpoint is older than `beaconTimestamp`
     * - Validator MUST be in `ACTIVE` status in the pod
     * - Validator MUST be slashed on the beacon chain
     */
    function verifyStaleBalance(
        uint64 beaconTimestamp,
        BeaconChainProofs.StateRootProof calldata stateRootProof,
        BeaconChainProofs.ValidatorProof calldata proof
    ) external;

    /// @notice Allows the owner or proof submitter to initiate one or more requests to
    /// consolidate their validators on the beacon chain.
    /// @param requests An array of requests consisting of the source and target pubkeys
    /// of the validators to be consolidated
    /// @dev The target validator MUST have ACTIVE (proven) withdrawal credentials pointed at
    /// the pod. This prevents cross-pod consolidations.
    /// @dev The consolidation request predeploy requires a fee is sent with each request;
    /// this is pulled from msg.value. After submitting all requests, any remaining fee is
    /// refunded to the caller by calling its fallback function.
    /// @dev This contract exposes `getConsolidationRequestFee` to query the current fee for
    /// a single request. If submitting multiple requests in a single block, the total fee
    /// is equal to (fee * requests.length). This fee is updated at the end of each block.
    ///
    /// (See https://eips.ethereum.org/EIPS/eip-7251#fee-calculation for details)
    ///
    /// @dev Note on beacon chain behavior:
    /// - If request.srcPubkey == request.targetPubkey, this is a "switch" consolidation. Once
    ///   processed on the beacon chain, the validator's withdrawal credentials will be changed
    ///   to compounding (0x02).
    /// - The rest of the notes assume src != target.
    /// - The target validator MUST already have 0x02 credentials. The source validator can have either.
    /// - Consolidation sets the source validator's exit_epoch and withdrawable_epoch, similar to an exit.
    ///   When the exit epoch is reached, an epoch sweep will process the consolidation and transfer balance
    ///   from the source to the target validator.
    /// - Consolidation transfers min(srcValidator.effective_balance, state.balance[srcIndex]) to the target.
    ///   This may not be the entirety of the source validator's balance; any remainder will be moved to the
    ///   pod when hit by a subsequent withdrawal sweep.
    ///
    /// @dev Note that consolidation requests CAN FAIL for a variety of reasons. Failures occur when the request
    /// is processed on the beacon chain, and are invisible to the pod. The pod and predeploy cannot guarantee
    /// a request will succeed; it's up to the pod owner to determine this for themselves. If your request fails,
    /// you can retry by initiating another request via this method.
    ///
    /// Some requirements that are NOT checked by the pod:
    /// - If request.srcPubkey == request.targetPubkey, the validator MUST have 0x01 credentials
    /// - If request.srcPubkey != request.targetPubkey, the target validator MUST have 0x02 credentials
    /// - Both the source and target validators MUST be active on the beacon chain and MUST NOT have
    ///   initiated exits
    /// - The source validator MUST NOT have pending partial withdrawal requests (via `requestWithdrawal`)
    /// - If the source validator is slashed after requesting consolidation (but before processing),
    ///   the consolidation will be skipped.
    ///
    /// For further reference, see consolidation processing at block and epoch boundaries:
    /// - Block: https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-process_consolidation_request
    /// - Epoch: https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-process_pending_consolidations
    function requestConsolidation(
        ConsolidationRequest[] calldata requests
    ) external payable;

    /// @notice Allows the owner or proof submitter to initiate one or more requests to
    /// withdraw funds from validators on the beacon chain.
    /// @param requests An array of requests consisting of the source validator and an
    /// amount to withdraw
    /// @dev The withdrawal request predeploy requires a fee is sent with each request;
    /// this is pulled from msg.value. After submitting all requests, any remaining fee is
    /// refunded to the caller by calling its fallback function.
    /// @dev This contract exposes `getWithdrawalRequestFee` to query the current fee for
    /// a single request. If submitting multiple requests in a single block, the total fee
    /// is equal to (fee * requests.length). This fee is updated at the end of each block.
    ///
    /// (See https://eips.ethereum.org/EIPS/eip-7002#fee-update-rule for details)
    ///
    /// @dev Note on beacon chain behavior:
    /// - Withdrawal requests have two types: full exit requests, and partial exit requests.
    ///   Partial exit requests will be skipped if the validator has 0x01 withdrawal credentials.
    ///   If you want your validators to have access to partial exits, use `requestConsolidation`
    ///   to change their withdrawal credentials to compounding (0x02).
    /// - If request.amount == 0, this is a FULL exit request. A full exit request initiates a
    ///   standard validator exit.
    /// - Other amounts are treated as PARTIAL exit requests. A partial exit request will NOT result
    ///   in a validator with less than 32 ETH balance. Any requested amount above this is ignored.
    /// - The actual amount withdrawn for a partial exit is given by the formula:
    ///   min(request.amount, state.balances[vIdx] - 32 ETH - pending_balance_to_withdraw)
    ///   (where `pending_balance_to_withdraw` is the sum of any outstanding partial exit requests)
    ///   (Note that this means you may request more than is actually withdrawn!)
    ///
    /// @dev Note that withdrawal requests CAN FAIL for a variety of reasons. Failures occur when the request
    /// is processed on the beacon chain, and are invisible to the pod. The pod and predeploy cannot guarantee
    /// a request will succeed; it's up to the pod owner to determine this for themselves. If your request fails,
    /// you can retry by initiating another request via this method.
    ///
    /// Some requirements that are NOT checked by the pod:
    /// - request.pubkey MUST be a valid validator pubkey
    /// - request.pubkey MUST belong to a validator whose withdrawal credentials are this pod
    /// - If request.amount is for a partial exit, the validator MUST have 0x02 withdrawal credentials
    /// - If request.amount is for a full exit, the validator MUST NOT have any pending partial exits
    /// - The validator MUST be active and MUST NOT have initiated exit
    ///
    /// For further reference: https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-process_withdrawal_request
    function requestWithdrawal(
        WithdrawalRequest[] calldata requests
    ) external payable;

    /// @notice called by owner of a pod to remove any ERC20s deposited in the pod
    function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external;

    /// @notice Allows the owner of a pod to update the proof submitter, a permissioned
    /// address that can call various EigenPod methods, but cannot trigger asset withdrawals
    /// from the DelegationManager.
    /// @dev Note that EITHER the podOwner OR proofSubmitter can access these methods,
    /// so it's fine to set your proofSubmitter to 0 if you want the podOwner to be the
    /// only address that can call these methods.
    /// @param newProofSubmitter The new proof submitter address. If set to 0, only the
    /// pod owner will be able to call EigenPod methods.
    function setProofSubmitter(
        address newProofSubmitter
    ) external;

    /**
     *
     *                                VIEW METHODS
     *
     */

    /// @notice An address with permissions to call `startCheckpoint` and `verifyWithdrawalCredentials`, set
    /// by the podOwner. This role exists to allow a podOwner to designate a hot wallet that can call
    /// these methods, allowing the podOwner to remain a cold wallet that is only used to manage funds.
    /// @dev If this address is NOT set, only the podOwner can call `startCheckpoint` and `verifyWithdrawalCredentials`
    function proofSubmitter() external view returns (address);

    /// @notice Native ETH in the pod that has been accounted for in a checkpoint (denominated in gwei).
    /// This amount is withdrawable from the pod via the DelegationManager withdrawal flow.
    function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);

    /// @notice The single EigenPodManager for EigenLayer
    function eigenPodManager() external view returns (IEigenPodManager);

    /// @notice The owner of this EigenPod
    function podOwner() external view returns (address);

    /// @notice Returns the validatorInfo struct for the provided pubkeyHash
    function validatorPubkeyHashToInfo(
        bytes32 validatorPubkeyHash
    ) external view returns (ValidatorInfo memory);

    /// @notice Returns the validatorInfo struct for the provided pubkey
    function validatorPubkeyToInfo(
        bytes calldata validatorPubkey
    ) external view returns (ValidatorInfo memory);

    /// @notice Returns the validator status for a given validator pubkey hash
    function validatorStatus(
        bytes32 pubkeyHash
    ) external view returns (VALIDATOR_STATUS);

    /// @notice Returns the validator status for a given validator pubkey
    function validatorStatus(
        bytes calldata validatorPubkey
    ) external view returns (VALIDATOR_STATUS);

    /// @notice Number of validators with proven withdrawal credentials, who do not have proven full withdrawals
    function activeValidatorCount() external view returns (uint256);

    /// @notice The timestamp of the last checkpoint finalized
    function lastCheckpointTimestamp() external view returns (uint64);

    /// @notice The timestamp of the currently-active checkpoint. Will be 0 if there is not active checkpoint
    function currentCheckpointTimestamp() external view returns (uint64);

    /// @notice Returns the currently-active checkpoint
    /// If there's not an active checkpoint, this method returns the checkpoint that was last active.
    function currentCheckpoint() external view returns (Checkpoint memory);

    /// @notice For each checkpoint, the total balance attributed to exited validators, in gwei
    ///
    /// NOTE that the values added to this mapping are NOT guaranteed to capture the entirety of a validator's
    /// exit - rather, they capture the total change in a validator's balance when a checkpoint shows their
    /// balance change from nonzero to zero. While a change from nonzero to zero DOES guarantee that a validator
    /// has been fully exited, it is possible that the magnitude of this change does not capture what is
    /// typically thought of as a "full exit."
    ///
    /// For example:
    /// 1. Consider a validator was last checkpointed at 32 ETH before exiting. Once the exit has been processed,
    /// it is expected that the validator's exited balance is calculated to be `32 ETH`.
    /// 2. However, before `startCheckpoint` is called, a deposit is made to the validator for 1 ETH. The beacon
    /// chain will automatically withdraw this ETH, but not until the withdrawal sweep passes over the validator
    /// again. Until this occurs, the validator's current balance (used for checkpointing) is 1 ETH.
    /// 3. If `startCheckpoint` is called at this point, the balance delta calculated for this validator will be
    /// `-31 ETH`, and because the validator has a nonzero balance, it is not marked WITHDRAWN.
    /// 4. After the exit is processed by the beacon chain, a subsequent `startCheckpoint` and checkpoint proof
    /// will calculate a balance delta of `-1 ETH` and attribute a 1 ETH exit to the validator.
    ///
    /// If this edge case impacts your usecase, it should be possible to mitigate this by monitoring for deposits
    /// to your exited validators, and waiting to call `startCheckpoint` until those deposits have been automatically
    /// exited.
    ///
    /// Additional edge cases this mapping does not cover:
    /// - If a validator is slashed, their balance exited will reflect their original balance rather than the slashed amount
    /// - The final partial withdrawal for an exited validator will be likely be included in this mapping.
    ///   i.e. if a validator was last checkpointed at 32.1 ETH before exiting, the next checkpoint will calculate their
    ///   "exited" amount to be 32.1 ETH rather than 32 ETH.
    function checkpointBalanceExitedGwei(
        uint64
    ) external view returns (uint64);

    /// @notice Query the 4788 oracle to get the parent block root of the slot with the given `timestamp`
    /// @param timestamp of the block for which the parent block root will be returned. MUST correspond
    /// to an existing slot within the last 24 hours. If the slot at `timestamp` was skipped, this method
    /// will revert.
    function getParentBlockRoot(
        uint64 timestamp
    ) external view returns (bytes32);

    /// @notice Returns the fee required to add a consolidation request to the EIP-7251 predeploy this block.
    /// @dev Note that the predeploy updates its fee every block according to https://eips.ethereum.org/EIPS/eip-7251#fee-calculation
    /// Consider overestimating the amount sent to ensure the fee does not update before your transaction.
    function getConsolidationRequestFee() external view returns (uint256);

    /// @notice Returns the current fee required to add a withdrawal request to the EIP-7002 predeploy.
    /// @dev Note that the predeploy updates its fee every block according to https://eips.ethereum.org/EIPS/eip-7002#fee-update-rule
    /// Consider overestimating the amount sent to ensure the fee does not update before your transaction.
    function getWithdrawalRequestFee() external view returns (uint256);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import "../interfaces/IPauserRegistry.sol";

/**
 * @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
 * @author Layr Labs, Inc.
 * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
 * @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
 * These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
 * @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
 * Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
 * For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
 * you can only flip (any number of) switches to off/0 (aka "paused").
 * If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
 * 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
 * 2) update the paused state to this new value
 * @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
 * indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
 */
interface IPausable {
    /// @dev Thrown when caller is not pauser.
    error OnlyPauser();
    /// @dev Thrown when caller is not unpauser.
    error OnlyUnpauser();
    /// @dev Thrown when currently paused.
    error CurrentlyPaused();
    /// @dev Thrown when invalid `newPausedStatus` is provided.
    error InvalidNewPausedStatus();
    /// @dev Thrown when a null address input is provided.
    error InputAddressZero();

    /// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
    event Paused(address indexed account, uint256 newPausedStatus);

    /// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
    event Unpaused(address indexed account, uint256 newPausedStatus);

    /// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
    function pauserRegistry() external view returns (IPauserRegistry);

    /**
     * @notice This function is used to pause an EigenLayer contract's functionality.
     * It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
     * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
     * @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
     */
    function pause(
        uint256 newPausedStatus
    ) external;

    /**
     * @notice Alias for `pause(type(uint256).max)`.
     */
    function pauseAll() external;

    /**
     * @notice This function is used to unpause an EigenLayer contract's functionality.
     * It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
     * @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
     * @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
     */
    function unpause(
        uint256 newPausedStatus
    ) external;

    /// @notice Returns the current paused status as a uint256.
    function paused() external view returns (uint256);

    /// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
    function paused(
        uint8 index
    ) external view returns (bool);
}

File 56 of 81 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 57 of 81 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)

pragma solidity >=0.4.16;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @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 ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile 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.
     *
     * NOTE: This function only supports 65-byte signatures. ERC-2098 short signatures are rejected. This restriction
     * is DEPRECATED and will be removed in v6.0. Developers SHOULD NOT use signatures as unique identifiers; use hash
     * invalidation or nonces for replay protection.
     *
     * 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 {MessageHashUtils-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]
     */
    function tryRecover(
        bytes32 hash,
        bytes memory signature
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        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.
            assembly ("memory-safe") {
                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, bytes32(signature.length));
        }
    }

    /**
     * @dev Variant of {tryRecover} that takes a signature in calldata
     */
    function tryRecoverCalldata(
        bytes32 hash,
        bytes calldata signature
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, calldata slices would work here, but are
            // significantly more expensive (length check) than using calldataload in assembly.
            assembly ("memory-safe") {
                r := calldataload(signature.offset)
                s := calldataload(add(signature.offset, 0x20))
                v := byte(0, calldataload(add(signature.offset, 0x40)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile 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.
     *
     * NOTE: This function only supports 65-byte signatures. ERC-2098 short signatures are rejected. This restriction
     * is DEPRECATED and will be removed in v6.0. Developers SHOULD NOT use signatures as unique identifiers; use hash
     * invalidation or nonces for replay protection.
     *
     * 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 {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Variant of {recover} that takes a signature in calldata
     */
    function recoverCalldata(bytes32 hash, bytes calldata signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecoverCalldata(hash, signature);
        _throwError(error, errorArg);
        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[ERC-2098 short signatures]
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            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.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
        // 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, s);
        }

        // 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, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @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, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Parse a signature into its `v`, `r` and `s` components. Supports 65-byte and 64-byte (ERC-2098)
     * formats. Returns (0,0,0) for invalid signatures. Consider skipping {tryRecover} or {recover} if so.
     */
    function parse(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
        assembly ("memory-safe") {
            // Check the signature length
            switch mload(signature)
            // - case 65: r,s,v signature (standard)
            case 65 {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098)
            case 64 {
                let vs := mload(add(signature, 0x40))
                r := mload(add(signature, 0x20))
                s := and(vs, shr(1, not(0)))
                v := add(shr(255, vs), 27)
            }
            default {
                r := 0
                s := 0
                v := 0
            }
        }
    }

    /**
     * @dev Variant of {parse} that takes a signature in calldata
     */
    function parseCalldata(bytes calldata signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
        assembly ("memory-safe") {
            // Check the signature length
            switch signature.length
            // - case 65: r,s,v signature (standard)
            case 65 {
                r := calldataload(signature.offset)
                s := calldataload(add(signature.offset, 0x20))
                v := byte(0, calldataload(add(signature.offset, 0x40)))
            }
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098)
            case 64 {
                let vs := calldataload(add(signature.offset, 0x20))
                r := calldataload(signature.offset)
                s := and(vs, shr(1, not(0)))
                v := add(shr(255, vs), 27)
            }
            default {
                r := 0
                s := 0
                v := 0
            }
        }
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.24;

import {Strings} from "../Strings.sol";

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an ERC-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32.
     */
    function toDataWithIntendedValidatorHash(
        address validator,
        bytes32 messageHash
    ) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            mstore(0x00, hex"19_00")
            mstore(0x02, shl(96, validator))
            mstore(0x16, messageHash)
            digest := keccak256(0x00, 0x36)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

File 60 of 81 : Arrays.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.

pragma solidity ^0.8.20;

import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";

/**
 * @dev Collection of functions related to array types.
 */
library Arrays {
    using SlotDerivation for bytes32;
    using StorageSlot for bytes32;

    /**
     * @dev Sort an array of uint256 (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        uint256[] memory array,
        function(uint256, uint256) pure returns (bool) comp
    ) internal pure returns (uint256[] memory) {
        _quickSort(_begin(array), _end(array), comp);
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of uint256 in increasing order.
     */
    function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
        sort(array, Comparators.lt);
        return array;
    }

    /**
     * @dev Sort an array of address (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        address[] memory array,
        function(address, address) pure returns (bool) comp
    ) internal pure returns (address[] memory) {
        sort(_castToUint256Array(array), _castToUint256Comp(comp));
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of address in increasing order.
     */
    function sort(address[] memory array) internal pure returns (address[] memory) {
        sort(_castToUint256Array(array), Comparators.lt);
        return array;
    }

    /**
     * @dev Sort an array of bytes32 (in memory) following the provided comparator function.
     *
     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
     *
     * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
     * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
     * consume more gas than is available in a block, leading to potential DoS.
     *
     * IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
     */
    function sort(
        bytes32[] memory array,
        function(bytes32, bytes32) pure returns (bool) comp
    ) internal pure returns (bytes32[] memory) {
        sort(_castToUint256Array(array), _castToUint256Comp(comp));
        return array;
    }

    /**
     * @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
     */
    function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
        sort(_castToUint256Array(array), Comparators.lt);
        return array;
    }

    /**
     * @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
     * at end (exclusive). Sorting follows the `comp` comparator.
     *
     * Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
     *
     * IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
     * be used only if the limits are within a memory array.
     */
    function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
        unchecked {
            if (end - begin < 0x40) return;

            // Use first element as pivot
            uint256 pivot = _mload(begin);
            // Position where the pivot should be at the end of the loop
            uint256 pos = begin;

            for (uint256 it = begin + 0x20; it < end; it += 0x20) {
                if (comp(_mload(it), pivot)) {
                    // If the value stored at the iterator's position comes before the pivot, we increment the
                    // position of the pivot and move the value there.
                    pos += 0x20;
                    _swap(pos, it);
                }
            }

            _swap(begin, pos); // Swap pivot into place
            _quickSort(begin, pos, comp); // Sort the left side of the pivot
            _quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
        }
    }

    /**
     * @dev Pointer to the memory location of the first element of `array`.
     */
    function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
        assembly ("memory-safe") {
            ptr := add(array, 0x20)
        }
    }

    /**
     * @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
     * that comes just after the last element of the array.
     */
    function _end(uint256[] memory array) private pure returns (uint256 ptr) {
        unchecked {
            return _begin(array) + array.length * 0x20;
        }
    }

    /**
     * @dev Load memory word (as a uint256) at location `ptr`.
     */
    function _mload(uint256 ptr) private pure returns (uint256 value) {
        assembly {
            value := mload(ptr)
        }
    }

    /**
     * @dev Swaps the elements memory location `ptr1` and `ptr2`.
     */
    function _swap(uint256 ptr1, uint256 ptr2) private pure {
        assembly {
            let value1 := mload(ptr1)
            let value2 := mload(ptr2)
            mstore(ptr1, value2)
            mstore(ptr2, value1)
        }
    }

    /// @dev Helper: low level cast address memory array to uint256 memory array
    function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast bytes32 memory array to uint256 memory array
    function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast address comp function to uint256 comp function
    function _castToUint256Comp(
        function(address, address) pure returns (bool) input
    ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
        assembly {
            output := input
        }
    }

    /// @dev Helper: low level cast bytes32 comp function to uint256 comp function
    function _castToUint256Comp(
        function(bytes32, bytes32) pure returns (bool) input
    ) private pure returns (function(uint256, uint256) pure returns (bool) output) {
        assembly {
            output := input
        }
    }

    /**
     * @dev Searches a sorted `array` and returns the first index that contains
     * a value greater or equal to `element`. If no such index exists (i.e. all
     * values in the array are strictly less than `element`), the array length is
     * returned. Time complexity O(log n).
     *
     * NOTE: The `array` is expected to be sorted in ascending order, and to
     * contain no repeated elements.
     *
     * IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
     * support for repeated elements in the array. The {lowerBound} function should
     * be used instead.
     */
    function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

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

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
        if (low > 0 && unsafeAccess(array, low - 1).value == element) {
            return low - 1;
        } else {
            return low;
        }
    }

    /**
     * @dev Searches an `array` sorted in ascending order and returns the first
     * index that contains a value greater or equal than `element`. If no such index
     * exists (i.e. all values in the array are strictly less than `element`), the array
     * length is returned. Time complexity O(log n).
     *
     * See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
     */
    function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

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

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value < element) {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            } else {
                high = mid;
            }
        }

        return low;
    }

    /**
     * @dev Searches an `array` sorted in ascending order and returns the first
     * index that contains a value strictly greater than `element`. If no such index
     * exists (i.e. all values in the array are strictly less than `element`), the array
     * length is returned. Time complexity O(log n).
     *
     * See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
     */
    function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

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

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeAccess(array, mid).value > element) {
                high = mid;
            } else {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            }
        }

        return low;
    }

    /**
     * @dev Same as {lowerBound}, but with an array in memory.
     */
    function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

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

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeMemoryAccess(array, mid) < element) {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            } else {
                high = mid;
            }
        }

        return low;
    }

    /**
     * @dev Same as {upperBound}, but with an array in memory.
     */
    function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
        uint256 low = 0;
        uint256 high = array.length;

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

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds towards zero (it does integer division with truncation).
            if (unsafeMemoryAccess(array, mid) > element) {
                high = mid;
            } else {
                // this cannot overflow because mid < high
                unchecked {
                    low = mid + 1;
                }
            }
        }

        return low;
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getAddressSlot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getBytes32Slot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getUint256Slot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getBytesSlot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
        bytes32 slot;
        assembly ("memory-safe") {
            slot := arr.slot
        }
        return slot.deriveArray().offset(pos).getStringSlot();
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
     *
     * WARNING: Only use if you are certain `pos` is lower than the array length.
     */
    function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
        assembly {
            res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(address[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(uint256[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(bytes[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }

    /**
     * @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
     *
     * WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
     */
    function unsafeSetLength(string[] storage array, uint256 len) internal {
        assembly ("memory-safe") {
            sstore(array.slot, len)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
 *
 * 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].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * 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 ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * Both values are immutable: they can only be set once during construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 default value returned by this function, unless
     * it's 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 returns (uint8) {
        return 18;
    }

    /// @inheritdoc IERC20
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /// @inheritdoc IERC20
    function balanceOf(address account) public view virtual 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 `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /// @inheritdoc IERC20
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` 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 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * 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 `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` 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.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` 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.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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 Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.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 `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
    }

    /**
     * @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);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

File 68 of 81 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        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
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        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
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        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
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        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
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        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
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        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
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        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
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @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
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @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
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @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
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @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
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @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
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @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
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @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
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @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
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @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
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @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
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @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
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @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
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @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
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @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
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @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
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @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
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @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
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @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
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @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
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @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
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @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
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @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
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @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
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @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
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @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
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @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
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @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
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @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
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @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
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @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
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

import "./Merkle.sol";
import "../libraries/Endian.sol";

//Utility library for parsing and PHASE0 beacon chain block headers
//SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
//BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
//BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate
library BeaconChainProofs {
    /// @dev Thrown when a proof is invalid.
    error InvalidProof();
    /// @dev Thrown when a proof with an invalid length is provided.
    error InvalidProofLength();
    /// @dev Thrown when a validator fields length is invalid.
    error InvalidValidatorFieldsLength();

    /// @notice Heights of various merkle trees in the beacon chain
    ///         beaconBlockRoot
    ///                |                              HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT
    ///         beaconStateRoot
    ///        /               \                      HEIGHT: BEACON_STATE_TREE_HEIGHT
    /// validatorContainerRoot, balanceContainerRoot
    ///          |                       |            HEIGHT: BALANCE_TREE_HEIGHT
    ///          |              individual balances
    ///          |                                    HEIGHT: VALIDATOR_TREE_HEIGHT
    /// individual validators
    uint256 internal constant BEACON_BLOCK_HEADER_TREE_HEIGHT = 3;
    uint256 internal constant DENEB_BEACON_STATE_TREE_HEIGHT = 5;
    uint256 internal constant PECTRA_BEACON_STATE_TREE_HEIGHT = 6;
    uint256 internal constant BALANCE_TREE_HEIGHT = 38;
    uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;

    /// @notice Index of the beaconStateRoot in the `BeaconBlockHeader` container
    ///
    /// BeaconBlockHeader = [..., state_root, ...]
    ///                      0...      3
    ///
    /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader)
    uint256 internal constant STATE_ROOT_INDEX = 3;

    /// @notice Indices for fields in the `BeaconState` container
    ///
    /// BeaconState = [..., validators, balances, ...]
    ///                0...     11         12
    ///
    /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate)
    uint256 internal constant VALIDATOR_CONTAINER_INDEX = 11;
    uint256 internal constant BALANCE_CONTAINER_INDEX = 12;

    /// @notice Number of fields in the `Validator` container
    /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator)
    uint256 internal constant VALIDATOR_FIELDS_LENGTH = 8;

    /// @notice Indices for fields in the `Validator` container
    uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
    uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
    uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
    uint256 internal constant VALIDATOR_SLASHED_INDEX = 3;
    uint256 internal constant VALIDATOR_ACTIVATION_EPOCH_INDEX = 5;
    uint256 internal constant VALIDATOR_EXIT_EPOCH_INDEX = 6;

    /// @notice Slot/Epoch timings
    uint64 internal constant SECONDS_PER_SLOT = 12;
    uint64 internal constant SLOTS_PER_EPOCH = 32;
    uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;

    /// @notice `FAR_FUTURE_EPOCH` is used as the default value for certain `Validator`
    /// fields when a `Validator` is first created on the beacon chain
    uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max;
    bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;

    /// @notice The beacon chain version to validate against
    enum ProofVersion {
        DENEB,
        PECTRA
    }

    /// @notice Contains a beacon state root and a merkle proof verifying its inclusion under a beacon block root
    struct StateRootProof {
        bytes32 beaconStateRoot;
        bytes proof;
    }

    /// @notice Contains a validator's fields and a merkle proof of their inclusion under a beacon state root
    struct ValidatorProof {
        bytes32[] validatorFields;
        bytes proof;
    }

    /// @notice Contains a beacon balance container root and a proof of this root under a beacon block root
    struct BalanceContainerProof {
        bytes32 balanceContainerRoot;
        bytes proof;
    }

    /// @notice Contains a validator balance root and a proof of its inclusion under a balance container root
    struct BalanceProof {
        bytes32 pubkeyHash;
        bytes32 balanceRoot;
        bytes proof;
    }

    /**
     *
     *              VALIDATOR FIELDS -> BEACON STATE ROOT -> BEACON BLOCK ROOT
     *
     */

    /// @notice Verify a merkle proof of the beacon state root against a beacon block root
    /// @param beaconBlockRoot merkle root of the beacon block
    /// @param proof the beacon state root and merkle proof of its inclusion under `beaconBlockRoot`
    function verifyStateRoot(bytes32 beaconBlockRoot, StateRootProof calldata proof) internal view {
        require(proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT), InvalidProofLength());

        /// This merkle proof verifies the `beaconStateRoot` under the `beaconBlockRoot`
        /// - beaconBlockRoot
        /// |                            HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT
        /// -- beaconStateRoot
        require(
            Merkle.verifyInclusionSha256({
                proof: proof.proof,
                root: beaconBlockRoot,
                leaf: proof.beaconStateRoot,
                index: STATE_ROOT_INDEX
            }),
            InvalidProof()
        );
    }

    /// @notice Verify a merkle proof of a validator container against a `beaconStateRoot`
    /// @dev This proof starts at a validator's container root, proves through the validator container root,
    /// and continues proving to the root of the `BeaconState`
    /// @dev See https://eth2book.info/capella/part3/containers/dependencies/#validator for info on `Validator` containers
    /// @dev See https://eth2book.info/capella/part3/containers/state/#beaconstate for info on `BeaconState` containers
    /// @param beaconStateRoot merkle root of the `BeaconState` container
    /// @param validatorFields an individual validator's fields. These are merklized to form a `validatorRoot`,
    /// which is used as the leaf to prove against `beaconStateRoot`
    /// @param validatorFieldsProof a merkle proof of inclusion of `validatorFields` under `beaconStateRoot`
    /// @param validatorIndex the validator's unique index
    function verifyValidatorFields(
        ProofVersion proofVersion,
        bytes32 beaconStateRoot,
        bytes32[] calldata validatorFields,
        bytes calldata validatorFieldsProof,
        uint40 validatorIndex
    ) internal view {
        require(validatorFields.length == VALIDATOR_FIELDS_LENGTH, InvalidValidatorFieldsLength());

        uint256 beaconStateTreeHeight = getBeaconStateTreeHeight(proofVersion);

        /// Note: the reason we use `VALIDATOR_TREE_HEIGHT + 1` here is because the merklization process for
        /// this container includes hashing the root of the validator tree with the length of the validator list
        require(
            validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + beaconStateTreeHeight),
            InvalidProofLength()
        );

        // Merkleize `validatorFields` to get the leaf to prove
        bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields);

        /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees:
        /// - beaconStateRoot
        /// |                            HEIGHT: BEACON_STATE_TREE_HEIGHT
        /// -- validatorContainerRoot
        /// |                            HEIGHT: VALIDATOR_TREE_HEIGHT + 1
        /// ---- validatorRoot
        uint256 index = (VALIDATOR_CONTAINER_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex);

        require(
            Merkle.verifyInclusionSha256({
                proof: validatorFieldsProof,
                root: beaconStateRoot,
                leaf: validatorRoot,
                index: index
            }),
            InvalidProof()
        );
    }

    /**
     *
     *          VALIDATOR BALANCE -> BALANCE CONTAINER ROOT -> BEACON BLOCK ROOT
     *
     */

    /// @notice Verify a merkle proof of the beacon state's balances container against the beacon block root
    /// @dev This proof starts at the balance container root, proves through the beacon state root, and
    /// continues proving through the beacon block root. As a result, this proof will contain elements
    /// of a `StateRootProof` under the same block root, with the addition of proving the balances field
    /// within the beacon state.
    /// @dev This is used to make checkpoint proofs more efficient, as a checkpoint will verify multiple balances
    /// against the same balance container root.
    /// @param beaconBlockRoot merkle root of the beacon block
    /// @param proof a beacon balance container root and merkle proof of its inclusion under `beaconBlockRoot`
    function verifyBalanceContainer(
        ProofVersion proofVersion,
        bytes32 beaconBlockRoot,
        BalanceContainerProof calldata proof
    ) internal view {
        uint256 beaconStateTreeHeight = getBeaconStateTreeHeight(proofVersion);

        require(
            proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + beaconStateTreeHeight), InvalidProofLength()
        );

        /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees:
        /// - beaconBlockRoot
        /// |                            HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT
        /// -- beaconStateRoot
        /// |                            HEIGHT: BEACON_STATE_TREE_HEIGHT
        /// ---- balancesContainerRoot
        uint256 index = (STATE_ROOT_INDEX << (beaconStateTreeHeight)) | BALANCE_CONTAINER_INDEX;

        require(
            Merkle.verifyInclusionSha256({
                proof: proof.proof,
                root: beaconBlockRoot,
                leaf: proof.balanceContainerRoot,
                index: index
            }),
            InvalidProof()
        );
    }

    /// @notice Verify a merkle proof of a validator's balance against the beacon state's `balanceContainerRoot`
    /// @param balanceContainerRoot the merkle root of all validators' current balances
    /// @param validatorIndex the index of the validator whose balance we are proving
    /// @param proof the validator's associated balance root and a merkle proof of inclusion under `balanceContainerRoot`
    /// @return validatorBalanceGwei the validator's current balance (in gwei)
    function verifyValidatorBalance(
        bytes32 balanceContainerRoot,
        uint40 validatorIndex,
        BalanceProof calldata proof
    ) internal view returns (uint64 validatorBalanceGwei) {
        /// Note: the reason we use `BALANCE_TREE_HEIGHT + 1` here is because the merklization process for
        /// this container includes hashing the root of the balances tree with the length of the balances list
        require(proof.proof.length == 32 * (BALANCE_TREE_HEIGHT + 1), InvalidProofLength());

        /// When merkleized, beacon chain balances are combined into groups of 4 called a `balanceRoot`. The merkle
        /// proof here verifies that this validator's `balanceRoot` is included in the `balanceContainerRoot`
        /// - balanceContainerRoot
        /// |                            HEIGHT: BALANCE_TREE_HEIGHT
        /// -- balanceRoot
        uint256 balanceIndex = uint256(validatorIndex / 4);

        require(
            Merkle.verifyInclusionSha256({
                proof: proof.proof,
                root: balanceContainerRoot,
                leaf: proof.balanceRoot,
                index: balanceIndex
            }),
            InvalidProof()
        );

        /// Extract the individual validator's balance from the `balanceRoot`
        return getBalanceAtIndex(proof.balanceRoot, validatorIndex);
    }

    /**
     * @notice Parses a balanceRoot to get the uint64 balance of a validator.
     * @dev During merkleization of the beacon state balance tree, four uint64 values are treated as a single
     * leaf in the merkle tree. We use validatorIndex % 4 to determine which of the four uint64 values to
     * extract from the balanceRoot.
     * @param balanceRoot is the combination of 4 validator balances being proven for
     * @param validatorIndex is the index of the validator being proven for
     * @return The validator's balance, in Gwei
     */
    function getBalanceAtIndex(bytes32 balanceRoot, uint40 validatorIndex) internal pure returns (uint64) {
        uint256 bitShiftAmount = (validatorIndex % 4) * 64;
        return Endian.fromLittleEndianUint64(bytes32((uint256(balanceRoot) << bitShiftAmount)));
    }

    /// @notice Indices for fields in the `Validator` container:
    /// 0: pubkey
    /// 1: withdrawal credentials
    /// 2: effective balance
    /// 3: slashed?
    /// 4: activation eligibility epoch
    /// 5: activation epoch
    /// 6: exit epoch
    /// 7: withdrawable epoch
    ///
    /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator)

    /// @dev Retrieves a validator's pubkey hash
    function getPubkeyHash(
        bytes32[] memory validatorFields
    ) internal pure returns (bytes32) {
        return validatorFields[VALIDATOR_PUBKEY_INDEX];
    }

    /// @dev Retrieves a validator's withdrawal credentials
    function getWithdrawalCredentials(
        bytes32[] memory validatorFields
    ) internal pure returns (bytes32) {
        return validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
    }

    /// @dev Retrieves a validator's effective balance (in gwei)
    function getEffectiveBalanceGwei(
        bytes32[] memory validatorFields
    ) internal pure returns (uint64) {
        return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
    }

    /// @dev Retrieves a validator's activation epoch
    function getActivationEpoch(
        bytes32[] memory validatorFields
    ) internal pure returns (uint64) {
        return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_ACTIVATION_EPOCH_INDEX]);
    }

    /// @dev Retrieves true IFF a validator is marked slashed
    function isValidatorSlashed(
        bytes32[] memory validatorFields
    ) internal pure returns (bool) {
        return validatorFields[VALIDATOR_SLASHED_INDEX] != 0;
    }

    /// @dev Retrieves a validator's exit epoch
    function getExitEpoch(
        bytes32[] memory validatorFields
    ) internal pure returns (uint64) {
        return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_EXIT_EPOCH_INDEX]);
    }

    /// @dev We check if the proofTimestamp is <= pectraForkTimestamp because a `proofTimestamp` at the `pectraForkTimestamp`
    ///      is considered to be Pre-Pectra given the EIP-4788 oracle returns the parent block.
    function getBeaconStateTreeHeight(
        ProofVersion proofVersion
    ) internal pure returns (uint256) {
        return proofVersion == ProofVersion.DENEB ? DENEB_BEACON_STATE_TREE_HEIGHT : PECTRA_BEACON_STATE_TREE_HEIGHT;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol)

pragma solidity ^0.8.24;

import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
import {Bytes} from "./Bytes.sol";

/**
 * @dev String operations.
 */
library Strings {
    using SafeCast for *;

    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;
    uint256 private constant SPECIAL_CHARS_LOOKUP =
        (1 << 0x08) | // backspace
            (1 << 0x09) | // tab
            (1 << 0x0a) | // newline
            (1 << 0x0c) | // form feed
            (1 << 0x0d) | // carriage return
            (1 << 0x22) | // double quote
            (1 << 0x5c); // backslash

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev The string being parsed contains characters that are not in scope of the given base.
     */
    error StringsInvalidChar();

    /**
     * @dev The string being parsed is not a properly formatted address.
     */
    error StringsInvalidAddressFormat();

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(add(buffer, 0x20), length)
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.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) {
        uint256 localValue = value;
        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] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        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);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
     * representation, according to EIP-55.
     */
    function toChecksumHexString(address addr) internal pure returns (string memory) {
        bytes memory buffer = bytes(toHexString(addr));

        // hash the hex part of buffer (skip length + 2 bytes, length 40)
        uint256 hashValue;
        assembly ("memory-safe") {
            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
        }

        for (uint256 i = 41; i > 1; --i) {
            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
                // case shift by xoring with 0x20
                buffer[i] ^= 0x20;
            }
            hashValue >>= 4;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `bytes` buffer to its ASCII `string` hexadecimal representation.
     */
    function toHexString(bytes memory input) internal pure returns (string memory) {
        unchecked {
            bytes memory buffer = new bytes(2 * input.length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 0; i < input.length; ++i) {
                uint8 v = uint8(input[i]);
                buffer[2 * i + 2] = HEX_DIGITS[v >> 4];
                buffer[2 * i + 3] = HEX_DIGITS[v & 0xf];
            }
            return string(buffer);
        }
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return Bytes.equal(bytes(a), bytes(b));
    }

    /**
     * @dev Parse a decimal string and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input) internal pure returns (uint256) {
        return parseUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        uint256 result = 0;
        for (uint256 i = begin; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 9) return (false, 0);
            result *= 10;
            result += chr;
        }
        return (true, result);
    }

    /**
     * @dev Parse a decimal string and returns the value as a `int256`.
     *
     * Requirements:
     * - The string must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input) internal pure returns (int256) {
        return parseInt(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
        (bool success, int256 value) = tryParseInt(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
     * the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
        return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
    }

    uint256 private constant ABS_MIN_INT256 = 2 ** 255;

    /**
     * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character or if the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, int256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseIntUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseIntUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, int256 value) {
        bytes memory buffer = bytes(input);

        // Check presence of a negative sign.
        bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        bool positiveSign = sign == bytes1("+");
        bool negativeSign = sign == bytes1("-");
        uint256 offset = (positiveSign || negativeSign).toUint();

        (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);

        if (absSuccess && absValue < ABS_MIN_INT256) {
            return (true, negativeSign ? -int256(absValue) : int256(absValue));
        } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
            return (true, type(int256).min);
        } else return (false, 0);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input) internal pure returns (uint256) {
        return parseHexUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseHexUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
     * invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseHexUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseHexUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        // skip 0x prefix if present
        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 offset = hasPrefix.toUint() * 2;

        uint256 result = 0;
        for (uint256 i = begin + offset; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 15) return (false, 0);
            result *= 16;
            unchecked {
                // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
                // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
                result += chr;
            }
        }
        return (true, result);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input) internal pure returns (address) {
        return parseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
        (bool success, address value) = tryParseAddress(input, begin, end);
        if (!success) revert StringsInvalidAddressFormat();
        return value;
    }

    /**
     * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
     * formatted address. See {parseAddress-string} requirements.
     */
    function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
        return tryParseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
     * formatted address. See {parseAddress-string-uint256-uint256} requirements.
     */
    function tryParseAddress(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, address value) {
        if (end > bytes(input).length || begin > end) return (false, address(0));

        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 expectedLength = 40 + hasPrefix.toUint() * 2;

        // check that input is the correct length
        if (end - begin == expectedLength) {
            // length guarantees that this does not overflow, and value is at most type(uint160).max
            (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
            return (s, address(uint160(v)));
        } else {
            return (false, address(0));
        }
    }

    function _tryParseChr(bytes1 chr) private pure returns (uint8) {
        uint8 value = uint8(chr);

        // Try to parse `chr`:
        // - Case 1: [0-9]
        // - Case 2: [a-f]
        // - Case 3: [A-F]
        // - otherwise not supported
        unchecked {
            if (value > 47 && value < 58) value -= 48;
            else if (value > 96 && value < 103) value -= 87;
            else if (value > 64 && value < 71) value -= 55;
            else return type(uint8).max;
        }

        return value;
    }

    /**
     * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
     *
     * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
     *
     * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
     * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
     * characters that are not in this range, but other tooling may provide different results.
     */
    function escapeJSON(string memory input) internal pure returns (string memory) {
        bytes memory buffer = bytes(input);
        bytes memory output = new bytes(2 * buffer.length); // worst case scenario
        uint256 outputLength = 0;

        for (uint256 i; i < buffer.length; ++i) {
            bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
            if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
                output[outputLength++] = "\\";
                if (char == 0x08) output[outputLength++] = "b";
                else if (char == 0x09) output[outputLength++] = "t";
                else if (char == 0x0a) output[outputLength++] = "n";
                else if (char == 0x0c) output[outputLength++] = "f";
                else if (char == 0x0d) output[outputLength++] = "r";
                else if (char == 0x5c) output[outputLength++] = "\\";
                else if (char == 0x22) {
                    // solhint-disable-next-line quotes
                    output[outputLength++] = '"';
                }
            } else {
                output[outputLength++] = char;
            }
        }
        // write the actual length and deallocate unused memory
        assembly ("memory-safe") {
            mstore(output, outputLength)
            mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
        }

        return string(output);
    }

    /**
     * @dev Reads a bytes32 from a bytes array without bounds checking.
     *
     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
     * assembly block as such would prevent some optimizations.
     */
    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
        // This is not memory safe in the general case, but all calls to this private function are within bounds.
        assembly ("memory-safe") {
            value := mload(add(add(buffer, 0x20), offset))
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides a set of functions to compare values.
 *
 * _Available since v5.1._
 */
library Comparators {
    function lt(uint256 a, uint256 b) internal pure returns (bool) {
        return a < b;
    }

    function gt(uint256 a, uint256 b) internal pure returns (bool) {
        return a > b;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
 * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
 * the solidity language / compiler.
 *
 * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
 *
 * Example usage:
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using StorageSlot for bytes32;
 *     using SlotDerivation for bytes32;
 *
 *     // Declare a namespace
 *     string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
 *
 *     function setValueInNamespace(uint256 key, address newValue) internal {
 *         _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
 *     }
 *
 *     function getValueInNamespace(uint256 key) internal view returns (address) {
 *         return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {StorageSlot}.
 *
 * NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
 * upgrade safety will ignore the slots accessed through this library.
 *
 * _Available since v5.1._
 */
library SlotDerivation {
    /**
     * @dev Derive an ERC-7201 slot from a string (namespace).
     */
    function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
        assembly ("memory-safe") {
            mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
            slot := and(keccak256(0x00, 0x20), not(0xff))
        }
    }

    /**
     * @dev Add an offset to a slot to get the n-th element of a structure or an array.
     */
    function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
        unchecked {
            return bytes32(uint256(slot) + pos);
        }
    }

    /**
     * @dev Derive the location of the first element in an array from the slot where the length is stored.
     */
    function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, slot)
            result := keccak256(0x00, 0x20)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, and(key, shr(96, not(0))))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, iszero(iszero(key)))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)

pragma solidity >=0.8.4;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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 256, 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 << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMathUpgradeable {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, salt the leaves, or hash the leaves with a hash function other than
 * what is used for the Merkle tree's internal nodes. This is because the
 * concatenation of a sorted pair of internal nodes in the Merkle tree could
 * be reinterpreted as a leaf value.
 */
library Merkle {
    /// @notice Thrown when the provided proof was not a multiple of 32, or was empty for SHA256.
    /// @dev Error code: 0x4dc5f6a4
    error InvalidProofLength();

    /// @notice Thrown when the provided index was outside the max index for the tree.
    /// @dev Error code: 0x63df8171
    error InvalidIndex();

    /// @notice Thrown when the provided leaves' length was not a power of two.
    /// @dev Error code: 0xf6558f51
    error LeavesNotPowerOfTwo();

    /// @notice Thrown when the provided leaves' length was 0.
    /// @dev Error code: 0xbaec3d9a
    error NoLeaves();

    /// @notice Thrown when the provided leaves' length was insufficient.
    /// @dev Error code: 0xf8ef0367
    /// @dev This is used for the SHA256 Merkle tree, where the tree must have more than 1 leaf.
    error NotEnoughLeaves();

    /// @notice Thrown when the root is empty.
    /// @dev Error code: 0x53ce4ece
    /// @dev Empty roots should never be valid. We prevent them to avoid issues like the Nomad bridge attack: <https://medium.com/nomad-xyz-blog/nomad-bridge-hack-root-cause-analysis-875ad2e5aacd>
    error EmptyRoot();

    /**
     * @notice Verifies that a given leaf is included in a Merkle tree
     * @param proof The proof of inclusion for the leaf
     * @param root The root of the Merkle tree
     * @param leaf The leaf to verify
     * @param index The index of the leaf in the Merkle tree
     * @return True if the leaf is included in the Merkle tree, false otherwise
     * @dev A `proof` is valid if and only if the rebuilt hash matches the root of the tree.
     * @dev Reverts for:
     *      - InvalidProofLength: proof.length is not a multiple of 32.
     *      - InvalidIndex: index is not 0 at conclusion of computation (implying outside the max index for the tree).
     */
    function verifyInclusionKeccak(
        bytes memory proof,
        bytes32 root,
        bytes32 leaf,
        uint256 index
    ) internal pure returns (bool) {
        require(root != bytes32(0), EmptyRoot());
        return processInclusionProofKeccak(proof, leaf, index) == root;
    }

    /**
     * @notice Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`.
     * @param proof The proof of inclusion for the leaf
     * @param leaf The leaf to verify
     * @param index The index of the leaf in the Merkle tree
     * @return The rebuilt hash
     * @dev Reverts for:
     *      - InvalidProofLength: proof.length is not a multiple of 32.
     *      - InvalidIndex: index is not 0 at conclusion of computation (implying outside the max index for the tree).
     * @dev The tree is built assuming `leaf` is the 0 indexed `index`'th leaf from the bottom left of the tree.
     */
    function processInclusionProofKeccak(
        bytes memory proof,
        bytes32 leaf,
        uint256 index
    ) internal pure returns (bytes32) {
        if (proof.length == 0) {
            return leaf;
        }

        require(proof.length % 32 == 0, InvalidProofLength());

        bytes32 computedHash = leaf;
        for (uint256 i = 32; i <= proof.length; i += 32) {
            if (index % 2 == 0) {
                // if index is even, then computedHash is a left sibling
                assembly {
                    mstore(0x00, computedHash)
                    mstore(0x20, mload(add(proof, i)))
                    computedHash := keccak256(0x00, 0x40)
                    index := div(index, 2)
                }
            } else {
                // if index is odd, then computedHash is a right sibling
                assembly {
                    mstore(0x00, mload(add(proof, i)))
                    mstore(0x20, computedHash)
                    computedHash := keccak256(0x00, 0x40)
                    index := div(index, 2)
                }
            }
        }

        // Confirm proof was fully consumed by end of computation
        require(index == 0, InvalidIndex());

        return computedHash;
    }

    /**
     * @notice Verifies that a given leaf is included in a Merkle tree
     * @param proof The proof of inclusion for the leaf
     * @param root The root of the Merkle tree
     * @param leaf The leaf to verify
     * @param index The index of the leaf in the Merkle tree
     * @return True if the leaf is included in the Merkle tree, false otherwise
     * @dev A `proof` is valid if and only if the rebuilt hash matches the root of the tree.
     * @dev Reverts for:
     *      - InvalidProofLength: proof.length is 0 or not a multiple of 32.
     *      - InvalidIndex: index is not 0 at conclusion of computation (implying outside the max index for the tree).
     */
    function verifyInclusionSha256(
        bytes memory proof,
        bytes32 root,
        bytes32 leaf,
        uint256 index
    ) internal view returns (bool) {
        require(root != bytes32(0), EmptyRoot());
        return processInclusionProofSha256(proof, leaf, index) == root;
    }

    /**
     * @notice Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`.
     * @param proof The proof of inclusion for the leaf
     * @param leaf The leaf to verify
     * @param index The index of the leaf in the Merkle tree
     * @return The rebuilt hash
     * @dev Reverts for:
     *      - InvalidProofLength: proof.length is 0 or not a multiple of 32.
     *      - InvalidIndex: index is not 0 at conclusion of computation (implying outside the max index for the tree).
     * @dev The tree is built assuming `leaf` is the 0 indexed `index`'th leaf from the bottom left of the tree.
     */
    function processInclusionProofSha256(
        bytes memory proof,
        bytes32 leaf,
        uint256 index
    ) internal view returns (bytes32) {
        require(proof.length != 0 && proof.length % 32 == 0, InvalidProofLength());
        bytes32[1] memory computedHash = [leaf];
        for (uint256 i = 32; i <= proof.length; i += 32) {
            if (index % 2 == 0) {
                // if index is even, then computedHash is a left sibling
                assembly {
                    mstore(0x00, mload(computedHash))
                    mstore(0x20, mload(add(proof, i)))
                    if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) }
                    index := div(index, 2)
                }
            } else {
                // if index is odd, then computedHash is a right sibling
                assembly {
                    mstore(0x00, mload(add(proof, i)))
                    mstore(0x20, mload(computedHash))
                    if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) }
                    index := div(index, 2)
                }
            }
        }

        // Confirm proof was fully consumed by end of computation
        require(index == 0, InvalidIndex());

        return computedHash[0];
    }

    /**
     * @notice Returns the Merkle root of a tree created from a set of leaves using SHA-256 as its hash function
     * @param leaves the leaves of the Merkle tree
     * @return The computed Merkle root of the tree.
     * @dev Reverts for:
     *      - NotEnoughLeaves: leaves.length is less than 2.
     *      - LeavesNotPowerOfTwo: leaves.length is not a power of two.
     * @dev Unlike the Keccak version, this function does not allow a single-leaf tree.
     */
    function merkleizeSha256(
        bytes32[] memory leaves
    ) internal pure returns (bytes32) {
        require(leaves.length > 1, NotEnoughLeaves());
        require(isPowerOfTwo(leaves.length), LeavesNotPowerOfTwo());

        // There are half as many nodes in the layer above the leaves
        uint256 numNodesInLayer = leaves.length / 2;
        // Create a layer to store the internal nodes
        bytes32[] memory layer = new bytes32[](numNodesInLayer);
        // Fill the layer with the pairwise hashes of the leaves
        for (uint256 i = 0; i < numNodesInLayer; i++) {
            layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
        }

        // While we haven't computed the root
        while (numNodesInLayer != 1) {
            // The next layer above has half as many nodes
            numNodesInLayer /= 2;
            // Overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
            for (uint256 i = 0; i < numNodesInLayer; i++) {
                layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
            }
        }
        // The first node in the layer is the root
        return layer[0];
    }

    /**
     * @notice Returns the Merkle root of a tree created from a set of leaves using Keccak as its hash function
     * @param leaves the leaves of the Merkle tree
     * @return The computed Merkle root of the tree.
     * @dev Reverts for:
     *      - NoLeaves: leaves.length is 0.
     */
    function merkleizeKeccak(
        bytes32[] memory leaves
    ) internal pure returns (bytes32) {
        require(leaves.length > 0, NoLeaves());

        uint256 numNodesInLayer;
        if (!isPowerOfTwo(leaves.length)) {
            // Pad to the next power of 2
            numNodesInLayer = 1;
            while (numNodesInLayer < leaves.length) {
                numNodesInLayer *= 2;
            }
        } else {
            numNodesInLayer = leaves.length;
        }

        // Create a layer to store the internal nodes
        bytes32[] memory layer = new bytes32[](numNodesInLayer);
        for (uint256 i = 0; i < leaves.length; i++) {
            layer[i] = leaves[i];
        }

        // While we haven't computed the root
        while (numNodesInLayer != 1) {
            // The next layer above has half as many nodes
            numNodesInLayer /= 2;
            // Overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
            for (uint256 i = 0; i < numNodesInLayer; i++) {
                layer[i] = keccak256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
            }
        }
        // The first node in the layer is the root
        return layer[0];
    }

    /**
     * @notice Returns the Merkle proof for a given index in a tree created from a set of leaves using Keccak as its hash function
     * @param leaves the leaves of the Merkle tree
     * @param index the index of the leaf to get the proof for
     * @return proof The computed Merkle proof for the leaf at index.
     * @dev Reverts for:
     *      - InvalidIndex: index is outside the max index for the tree.
     */
    function getProofKeccak(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory proof) {
        require(leaves.length > 0, NoLeaves());
        // TODO: very inefficient, use ZERO_HASHES
        // pad to the next power of 2
        uint256 numNodesInLayer = 1;
        while (numNodesInLayer < leaves.length) {
            numNodesInLayer *= 2;
        }
        bytes32[] memory layer = new bytes32[](numNodesInLayer);
        for (uint256 i = 0; i < leaves.length; i++) {
            layer[i] = leaves[i];
        }

        if (index >= layer.length) revert InvalidIndex();

        // While we haven't computed the root
        while (numNodesInLayer != 1) {
            // Flip the least significant bit of index to get the sibling index
            uint256 siblingIndex = index ^ 1;
            // Add the sibling to the proof
            proof = abi.encodePacked(proof, layer[siblingIndex]);
            index /= 2;

            // The next layer above has half as many nodes
            numNodesInLayer /= 2;
            // Overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
            for (uint256 i = 0; i < numNodesInLayer; i++) {
                layer[i] = keccak256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
            }
        }
    }

    /**
     * @notice Returns the Merkle proof for a given index in a tree created from a set of leaves using SHA-256 as its hash function
     * @param leaves the leaves of the Merkle tree
     * @param index the index of the leaf to get the proof for
     * @return proof The computed Merkle proof for the leaf at index.
     * @dev Reverts for:
     *      - NotEnoughLeaves: leaves.length is less than 2.
     * @dev Unlike the Keccak version, this function does not allow a single-leaf proof.
     */
    function getProofSha256(bytes32[] memory leaves, uint256 index) internal pure returns (bytes memory proof) {
        require(leaves.length > 1, NotEnoughLeaves());
        // TODO: very inefficient, use ZERO_HASHES
        // pad to the next power of 2
        uint256 numNodesInLayer = 1;
        while (numNodesInLayer < leaves.length) {
            numNodesInLayer *= 2;
        }
        bytes32[] memory layer = new bytes32[](numNodesInLayer);
        for (uint256 i = 0; i < leaves.length; i++) {
            layer[i] = leaves[i];
        }

        if (index >= layer.length) revert InvalidIndex();

        // While we haven't computed the root
        while (numNodesInLayer != 1) {
            // Flip the least significant bit of index to get the sibling index
            uint256 siblingIndex = index ^ 1;
            // Add the sibling to the proof
            proof = abi.encodePacked(proof, layer[siblingIndex]);
            index /= 2;

            // The next layer above has half as many nodes
            numNodesInLayer /= 2;
            // Overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
            for (uint256 i = 0; i < numNodesInLayer; i++) {
                layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
            }
        }
    }

    /**
     * @notice Returns whether the input is a power of two
     * @param value the value to check
     * @return True if the input is a power of two, false otherwise
     */
    function isPowerOfTwo(
        uint256 value
    ) internal pure returns (bool) {
        return value != 0 && (value & (value - 1)) == 0;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library Endian {
    /**
     * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
     * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
     * @return n The big endian-formatted uint64
     * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
     * through a right-shift/shr operation.
     */
    function fromLittleEndianUint64(
        bytes32 lenum
    ) internal pure returns (uint64 n) {
        // the number needs to be stored in little-endian encoding (ie in bytes 0-8)
        n = uint64(uint256(lenum >> 192));
        // forgefmt: disable-next-item
        return (n >> 56) | 
            ((0x00FF000000000000 & n) >> 40) | 
            ((0x0000FF0000000000 & n) >> 24) | 
            ((0x000000FF00000000 & n) >> 8)  | 
            ((0x00000000FF000000 & n) << 8)  | 
            ((0x0000000000FF0000 & n) << 24) | 
            ((0x000000000000FF00 & n) << 40) | 
            ((0x00000000000000FF & n) << 56);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
        }
    }

    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
            // taking advantage of the most significant (or "sign" bit) in two's complement representation.
            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
            // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
            int256 mask = n >> 255;

            // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
            return uint256((n + mask) ^ mask);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Bytes.sol)

pragma solidity ^0.8.24;

import {Math} from "./math/Math.sol";

/**
 * @dev Bytes operations.
 */
library Bytes {
    /**
     * @dev Forward search for `s` in `buffer`
     * * If `s` is present in the buffer, returns the index of the first instance
     * * If `s` is not present in the buffer, returns type(uint256).max
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
     */
    function indexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
        return indexOf(buffer, s, 0);
    }

    /**
     * @dev Forward search for `s` in `buffer` starting at position `pos`
     * * If `s` is present in the buffer (at or after `pos`), returns the index of the next instance
     * * If `s` is not present in the buffer (at or after `pos`), returns type(uint256).max
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
     */
    function indexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
        uint256 length = buffer.length;
        for (uint256 i = pos; i < length; ++i) {
            if (bytes1(_unsafeReadBytesOffset(buffer, i)) == s) {
                return i;
            }
        }
        return type(uint256).max;
    }

    /**
     * @dev Backward search for `s` in `buffer`
     * * If `s` is present in the buffer, returns the index of the last instance
     * * If `s` is not present in the buffer, returns type(uint256).max
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
     */
    function lastIndexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
        return lastIndexOf(buffer, s, type(uint256).max);
    }

    /**
     * @dev Backward search for `s` in `buffer` starting at position `pos`
     * * If `s` is present in the buffer (at or before `pos`), returns the index of the previous instance
     * * If `s` is not present in the buffer (at or before `pos`), returns type(uint256).max
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
     */
    function lastIndexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
        unchecked {
            uint256 length = buffer.length;
            for (uint256 i = Math.min(Math.saturatingAdd(pos, 1), length); i > 0; --i) {
                if (bytes1(_unsafeReadBytesOffset(buffer, i - 1)) == s) {
                    return i - 1;
                }
            }
            return type(uint256).max;
        }
    }

    /**
     * @dev Copies the content of `buffer`, from `start` (included) to the end of `buffer` into a new bytes object in
     * memory.
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice[Javascript's `Array.slice`]
     */
    function slice(bytes memory buffer, uint256 start) internal pure returns (bytes memory) {
        return slice(buffer, start, buffer.length);
    }

    /**
     * @dev Copies the content of `buffer`, from `start` (included) to `end` (excluded) into a new bytes object in
     * memory. The `end` argument is truncated to the length of the `buffer`.
     *
     * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice[Javascript's `Array.slice`]
     */
    function slice(bytes memory buffer, uint256 start, uint256 end) internal pure returns (bytes memory) {
        // sanitize
        uint256 length = buffer.length;
        end = Math.min(end, length);
        start = Math.min(start, end);

        // allocate and copy
        bytes memory result = new bytes(end - start);
        assembly ("memory-safe") {
            mcopy(add(result, 0x20), add(add(buffer, 0x20), start), sub(end, start))
        }

        return result;
    }

    /**
     * @dev Moves the content of `buffer`, from `start` (included) to the end of `buffer` to the start of that buffer.
     *
     * NOTE: This function modifies the provided buffer in place. If you need to preserve the original buffer, use {slice} instead
     */
    function splice(bytes memory buffer, uint256 start) internal pure returns (bytes memory) {
        return splice(buffer, start, buffer.length);
    }

    /**
     * @dev Moves the content of `buffer`, from `start` (included) to end (excluded) to the start of that buffer. The
     * `end` argument is truncated to the length of the `buffer`.
     *
     * NOTE: This function modifies the provided buffer in place. If you need to preserve the original buffer, use {slice} instead
     */
    function splice(bytes memory buffer, uint256 start, uint256 end) internal pure returns (bytes memory) {
        // sanitize
        uint256 length = buffer.length;
        end = Math.min(end, length);
        start = Math.min(start, end);

        // allocate and copy
        assembly ("memory-safe") {
            mcopy(add(buffer, 0x20), add(add(buffer, 0x20), start), sub(end, start))
            mstore(buffer, sub(end, start))
        }

        return buffer;
    }

    /**
     * @dev Concatenate an array of bytes into a single bytes object.
     *
     * For fixed bytes types, we recommend using the solidity built-in `bytes.concat` or (equivalent)
     * `abi.encodePacked`.
     *
     * NOTE: this could be done in assembly with a single loop that expands starting at the FMP, but that would be
     * significantly less readable. It might be worth benchmarking the savings of the full-assembly approach.
     */
    function concat(bytes[] memory buffers) internal pure returns (bytes memory) {
        uint256 length = 0;
        for (uint256 i = 0; i < buffers.length; ++i) {
            length += buffers[i].length;
        }

        bytes memory result = new bytes(length);

        uint256 offset = 0x20;
        for (uint256 i = 0; i < buffers.length; ++i) {
            bytes memory input = buffers[i];
            assembly ("memory-safe") {
                mcopy(add(result, offset), add(input, 0x20), mload(input))
            }
            unchecked {
                offset += input.length;
            }
        }

        return result;
    }

    /**
     * @dev Returns true if the two byte buffers are equal.
     */
    function equal(bytes memory a, bytes memory b) internal pure returns (bool) {
        return a.length == b.length && keccak256(a) == keccak256(b);
    }

    /**
     * @dev Reverses the byte order of a bytes32 value, converting between little-endian and big-endian.
     * Inspired in https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel[Reverse Parallel]
     */
    function reverseBytes32(bytes32 value) internal pure returns (bytes32) {
        value = // swap bytes
            ((value >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) |
            ((value & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
        value = // swap 2-byte long pairs
            ((value >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) |
            ((value & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
        value = // swap 4-byte long pairs
            ((value >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) |
            ((value & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
        value = // swap 8-byte long pairs
            ((value >> 64) & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) |
            ((value & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
        return (value >> 128) | (value << 128); // swap 16-byte long pairs
    }

    /// @dev Same as {reverseBytes32} but optimized for 128-bit values.
    function reverseBytes16(bytes16 value) internal pure returns (bytes16) {
        value = // swap bytes
            ((value & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) |
            ((value & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
        value = // swap 2-byte long pairs
            ((value & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) |
            ((value & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
        value = // swap 4-byte long pairs
            ((value & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) |
            ((value & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32);
        return (value >> 64) | (value << 64); // swap 8-byte long pairs
    }

    /// @dev Same as {reverseBytes32} but optimized for 64-bit values.
    function reverseBytes8(bytes8 value) internal pure returns (bytes8) {
        value = ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8); // swap bytes
        value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16); // swap 2-byte long pairs
        return (value >> 32) | (value << 32); // swap 4-byte long pairs
    }

    /// @dev Same as {reverseBytes32} but optimized for 32-bit values.
    function reverseBytes4(bytes4 value) internal pure returns (bytes4) {
        value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); // swap bytes
        return (value >> 16) | (value << 16); // swap 2-byte long pairs
    }

    /// @dev Same as {reverseBytes32} but optimized for 16-bit values.
    function reverseBytes2(bytes2 value) internal pure returns (bytes2) {
        return (value >> 8) | (value << 8);
    }

    /**
     * @dev Counts the number of leading zero bits a bytes array. Returns `8 * buffer.length`
     * if the buffer is all zeros.
     */
    function clz(bytes memory buffer) internal pure returns (uint256) {
        for (uint256 i = 0; i < buffer.length; i += 0x20) {
            bytes32 chunk = _unsafeReadBytesOffset(buffer, i);
            if (chunk != bytes32(0)) {
                return Math.min(8 * i + Math.clz(uint256(chunk)), 8 * buffer.length);
            }
        }
        return 8 * buffer.length;
    }

    /**
     * @dev Reads a bytes32 from a bytes array without bounds checking.
     *
     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
     * assembly block as such would prevent some optimizations.
     */
    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
        // This is not memory safe in the general case, but all calls to this private function are within bounds.
        assembly ("memory-safe") {
            value := mload(add(add(buffer, 0x20), offset))
        }
    }
}

Settings
{
  "remappings": [
    "openzeppelin-contracts/=lib/Aleph/lib/openzeppelin-contracts/",
    "@openzeppelin/contracts/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/",
    "@openzeppelin-upgrades/contracts/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/",
    "@aleph-script/=lib/Aleph/script/",
    "@aleph-test/=lib/Aleph/test/",
    "@aleph-vault/=lib/Aleph/src/",
    "@openzeppelin/contracts-upgradeable/=lib/Aleph/lib/openzeppelin-contracts-upgradeable/contracts/",
    "Aleph/=lib/Aleph/",
    "ds-test/=lib/eigenlayer-contracts/lib/ds-test/src/",
    "eigenlayer-contracts/=lib/eigenlayer-contracts/",
    "erc4626-tests/=lib/Aleph/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/Aleph/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
    "openzeppelin-contracts-upgradeable/=lib/Aleph/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
    "openzeppelin/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/",
    "zeus-templates/=lib/eigenlayer-contracts/lib/zeus-templates/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IAllocationManager","name":"_allocationManager","type":"address"},{"internalType":"contract IDelegationManager","name":"_delegationManager","type":"address"},{"internalType":"contract IStrategyManager","name":"_strategyManager","type":"address"},{"internalType":"contract IRewardsCoordinator","name":"_rewardsCoordinator","type":"address"},{"internalType":"contract IAlephVaultFactory","name":"_vaultFactory","type":"address"},{"internalType":"contract IStrategyFactory","name":"_strategyFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountTooSmall","type":"error"},{"inputs":[],"name":"AmountTooSmall","type":"error"},{"inputs":[],"name":"FlowIsCurrentlyPaused","type":"error"},{"inputs":[],"name":"FlowIsCurrentlyUnpaused","type":"error"},{"inputs":[],"name":"InsufficientAllocation","type":"error"},{"inputs":[],"name":"InsufficientAllocation","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualAmount","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"InsufficientOutput","type":"error"},{"inputs":[],"name":"InvalidAlephOperator","type":"error"},{"inputs":[],"name":"InvalidAllocationManager","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidClassId","type":"error"},{"inputs":[],"name":"InvalidDelegationManager","type":"error"},{"inputs":[],"name":"InvalidOperatorSet","type":"error"},{"inputs":[],"name":"InvalidRewardsCoordinator","type":"error"},{"inputs":[],"name":"InvalidStrategy","type":"error"},{"inputs":[],"name":"InvalidStrategy","type":"error"},{"inputs":[],"name":"InvalidStrategyFactory","type":"error"},{"inputs":[],"name":"InvalidStrategyManager","type":"error"},{"inputs":[],"name":"InvalidVault","type":"error"},{"inputs":[],"name":"InvalidVaultFactory","type":"error"},{"inputs":[],"name":"MagnitudeOverflow","type":"error"},{"inputs":[],"name":"MagnitudeOverflow","type":"error"},{"inputs":[],"name":"NoPendingUnallocation","type":"error"},{"inputs":[],"name":"NoTokensReceived","type":"error"},{"inputs":[],"name":"NotAlephOperator","type":"error"},{"inputs":[],"name":"NotMemberOfOperatorSet","type":"error"},{"inputs":[],"name":"NotRegisteredOperator","type":"error"},{"inputs":[],"name":"OperatorSplitNotZero","type":"error"},{"inputs":[],"name":"OperatorSplitNotZero","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TokenAmountMismatch","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VaultAlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"VaultNotInitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"alephVault","type":"address"},{"indexed":false,"internalType":"address","name":"originalStrategy","type":"address"},{"indexed":false,"internalType":"address","name":"slashedStrategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountToMint","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultShares","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"classId","type":"uint8"}],"name":"AllocatedToAlephVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4","name":"_pausableFlow","type":"bytes4"},{"indexed":false,"internalType":"address","name":"_pauser","type":"address"}],"name":"FlowPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4","name":"_pausableFlow","type":"bytes4"},{"indexed":false,"internalType":"address","name":"_unpauser","type":"address"}],"name":"FlowUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint32[]","name":"operatorSetIds","type":"uint32[]"}],"name":"OperatorDeregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint32[]","name":"operatorSetIds","type":"uint32[]"}],"name":"OperatorRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint32","name":"operatorSetId","type":"uint32"},{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"slashId","type":"uint256"}],"name":"SlashExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"},{"indexed":true,"internalType":"address","name":"alephVault","type":"address"},{"indexed":false,"internalType":"address","name":"originalStrategy","type":"address"},{"indexed":false,"internalType":"address","name":"slashedStrategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"classId","type":"uint8"}],"name":"UnallocateCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenHolder","type":"address"},{"indexed":true,"internalType":"address","name":"alephVault","type":"address"},{"indexed":false,"internalType":"address","name":"slashedStrategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"estAmountToRedeem","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"batchId","type":"uint48"},{"indexed":false,"internalType":"uint8","name":"classId","type":"uint8"}],"name":"UnallocateRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint8","name":"classId","type":"uint8"},{"indexed":true,"internalType":"address","name":"originalToken","type":"address"},{"indexed":true,"internalType":"address","name":"slashedToken","type":"address"},{"indexed":false,"internalType":"address","name":"slashedStrategy","type":"address"}],"name":"VaultInitialized","type":"event"},{"inputs":[],"name":"ALLOCATE_FLOW","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ALLOCATION_MANAGER","outputs":[{"internalType":"contract IAllocationManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATION_MANAGER","outputs":[{"internalType":"contract IDelegationManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_COORDINATOR","outputs":[{"internalType":"contract IRewardsCoordinator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STRATEGY_FACTORY","outputs":[{"internalType":"contract IStrategyFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STRATEGY_MANAGER","outputs":[{"internalType":"contract IStrategyManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNALLOCATE_FLOW","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FACTORY","outputs":[{"internalType":"contract IAlephVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_alephVault","type":"address"},{"components":[{"internalType":"uint8","name":"classId","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"bytes","name":"authSignature","type":"bytes"},{"internalType":"uint256","name":"expiryBlock","type":"uint256"}],"internalType":"struct AuthLibrary.AuthSignature","name":"authSignature","type":"tuple"}],"internalType":"struct IAlephVaultDeposit.RequestDepositParams","name":"_requestDepositParams","type":"tuple"}],"name":"allocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_alephVault","type":"address"}],"name":"calculateCompleteUnallocateAmount","outputs":[{"internalType":"uint256","name":"expectedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_alephVault","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"calculateUnallocateAmount","outputs":[{"internalType":"uint256","name":"_estimatedAmount","type":"uint256"},{"internalType":"contract IStrategy","name":"_strategy","type":"address"},{"internalType":"contract IERC20","name":"_token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_alephVault","type":"address"},{"internalType":"uint256","name":"_strategyDepositExpiry","type":"uint256"},{"internalType":"bytes","name":"_strategyDepositSignature","type":"bytes"}],"name":"completeUnallocate","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_v","type":"address"},{"internalType":"uint256","name":"_a","type":"uint256"}],"name":"correctWithdrawnAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_avs","type":"address"},{"internalType":"uint32[]","name":"_operatorSetIds","type":"uint32[]"}],"name":"deregisterOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"erc20Factory","outputs":[{"internalType":"contract IERC20Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_alephVault","type":"address"}],"name":"getPendingUnallocateStatus","outputs":[{"internalType":"uint256","name":"userPendingAmount","type":"uint256"},{"internalType":"uint256","name":"totalPendingAmount","type":"uint256"},{"internalType":"uint256","name":"redeemableAmount","type":"uint256"},{"internalType":"bool","name":"canComplete","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_guardian","type":"address"},{"internalType":"string","name":"_metadataURI","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_classId","type":"uint8"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"contract IStrategy","name":"_lstStrategy","type":"address"}],"name":"initializeVault","outputs":[{"internalType":"contract IStrategy","name":"_slashedStrategy","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_pausableFlow","type":"bytes4"}],"name":"isFlowPaused","outputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_pausableFlow","type":"bytes4"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_avs","type":"address"},{"internalType":"uint32[]","name":"_operatorSetIds","type":"uint32[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"registerOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_alephVault","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"requestUnallocate","outputs":[{"internalType":"uint48","name":"batchId","type":"uint48"},{"internalType":"uint256","name":"estAmountToRedeem","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_avs","type":"address"}],"name":"supportsAVS","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_pausableFlow","type":"bytes4"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultToOriginalStrategy","outputs":[{"internalType":"contract IStrategy","name":"_originalStrategy","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"vaultToSlashedStrategy","outputs":[{"internalType":"contract IStrategy","name":"_slashedStrategy","type":"address"}],"stateMutability":"view","type":"function"}]

610140346102d357601f61629638819003918201601f19168301916001600160401b038311848410176102d75780849260c0946040528339810103126102d3578051906001600160a01b038216908183036102d35760208101516001600160a01b03811693908481036102d35760408301516001600160a01b038116949092908584036102d3576060850151946001600160a01b0386168087036102d3576080820151976001600160a01b03891692838a036102d35760a00151996001600160a01b038b1694858c036102d35760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055156102c457156102b557156102a65715610297571561028857156102795760805260a05260c05260e05261010052610120525f5460ff8160081c166102245760ff808216036101ea575b604051615faa90816102ec82396080518181816107ed0152818161219901528181612c2701528181613e8a01526140b1015260a0518181816102650152610678015260c05181818161055401528181610c4701526127e4015260e0518181816105ea0152818161071e015281816111520152818161124701528181611271015281816112f3015261133e015261010051818181612f30015261352b0152610120518181816104ed015261312f0152f35b60ff90811916175f557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160ff8152a15f61013a565b60405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608490fd5b632f9b373360e11b5f5260045ffd5b63770f64bf60e11b5f5260045ffd5b63f8480a7560e01b5f5260045ffd5b6368b6c4bb60e01b5f5260045ffd5b63165cd34760e31b5f5260045ffd5b6307fb89e960e31b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe610160806040526004361015610013575f80fd5b5f610140525f3560e01c90816301ffc9a71461364a57508063049d4c1b14613585578063084727b11461355a578063103f290714613516578063117803e3146134ef57806313ce83c61461341e57806317f24c0f14612ebc5780631ac71d9614612dba578063248a9ca314612d875780632f2ff15d14612ce1578063303ca95614612c5657806331232bc914612c1257806336568abe14612b7e57806338b1b34e14612b495780633aa83ec714612a885780633ab22ff2146125f157806344260ff4146125cc5780634571e3a61461205c5780634cef3daf14611cb157806357b53c8f1461061957806371e2c264146105d257806391d14854146105835780639fc2c4761461053c578063a217fddf1461051c578063ae501345146104d5578063aed753c914610496578063b526578714610467578063bac1e94b1461038d578063c63fd502146102de578063d547741f14610294578063d5c1c20a1461024d578063efd96978146101fb578063f4c3066f146101c45763f5beff1014610198575f80fd5b346101bd57610140513660031901126101bd576040516340e1896760e01b8152602090f35b6101405180fd5b346101bd57610140513660031901126101bd575f516020615ef55f395f51905f52546040516001600160a01b039091168152602090f35b346101bd5760203660031901126101bd5760043563ffffffff60e01b81168091036101bd5761014051525f516020615f555f395f51905f52602052602060ff6040610140512054166040519015158152f35b346101bd57610140513660031901126101bd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101bd5760403660031901126101bd576102d76004356102b36136b3565b906102d26102cd825f526065602052600160405f20015490565b613d57565b61410c565b6101405180f35b346101bd5760803660031901126101bd576102f761369d565b6102ff6136b3565b6044356001600160401b0381116101bd5761031e9036906004016136c9565b90926064356001600160401b0381116101bd577f9efdc3d07eb312e06bf36ea85db02aec96817d7c7421f919027b240eaf34035d9361036461036b9236906004016136f9565b50506140ae565b61038360405192839260018060a01b03169583613984565b0390a26101405180f35b346101bd5760203660031901126101bd576004356001600160e01b031981168082036101bd576103bc81613d57565b8061014051525f516020615f555f395f51905f5260205260ff60406101405120541615610452576101408051919091525f516020615f555f395f51905f5260209081529051604090819020805460ff1916905580516001600160e01b031993909316835233918301919091527fc7e56e17b0a6c4b467df6495e1eda1baecd7ba20604e80c1058ac06f4578d85e91a16101405180f35b6368c87f3360e11b6101405152600461014051fd5b346101bd5760203660031901126101bd57602061048261369d565b6040516001600160a01b0390911630148152f35b346101bd5760403660031901126101bd576104bf6104b261369d565b6104ba613c5d565b61386d565b6104cc60243582546139ca565b90556101405180f35b346101bd57610140513660031901126101bd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101bd57610140513660031901126101bd576020604051610140518152f35b346101bd57610140513660031901126101bd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101bd5760403660031901126101bd5761059c6136b3565b60043561014051526065602052604061014051209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101bd57610140513660031901126101bd576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101bd5760403660031901126101bd5761063261369d565b60e0526001600160401b03602435116101bd576060602435360360031901126101bd5761065d61418d565b6040516336b87bd760e11b81523360048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115611b74576101405191611c92575b5015611c7d5763099b4ac360e31b61014051525f516020615f555f395f51905f5260205260ff604061014051205416611c68576106f160e051613a8c565b6024803501358015611c535760405163e063f81f60e01b81523360048201523060248201526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015611b74576101405190611c14575b61ffff915016611bff5760ff61076f60e0516138dd565b541660ff6107816024356004016139d7565b1603611bea5761079260e051613be6565b61010052506107a260e051613ac7565b60a0526040516107b181613726565b30815261014051602082019081526040516304c1b8eb60e31b815282516001600160a01b039081166004830152915163ffffffff1660248201527f00000000000000000000000000000000000000000000000000000000000000009091169290602081604481875afa908115611b74576101405191611bcb575b5015611bb6576040516333869dd160e11b815233600482015282516001600160a01b03166024820152602083015163ffffffff166044820152602081606481875afa908115611b74576101405191611b97575b5015611b8257604051906338f6b94760e21b8252600482015260208160248160018060a01b0361010051165afa908115611b74576101405191611b42575b508015611b2d5760409061095982516108d5848261378b565b60018152601f198401366020830137336108ee82613dc2565b526109436108fe610100516149e8565b85516315a29d4d60e11b815287516001600160a01b03166004820152602088015163ffffffff1660248201529390928492839291608060448501526084840190613de3565b8281036003190160648401526101405194613de3565b0381885afa908115611412576101405191611a78575b5061097c61098291613dc2565b51613dc2565b518181106119f75782519163021c373760e31b8352606083806109ad610100518933600485016144c7565b0381895afa928315611721576101405193611a48575b506001600160401b03916109da91838551166148ac565b915116808211611a40575b506001600160401b038111611a2b5760606001600160401b03610a2392169383518093819263021c373760e31b8352610100519033600485016144c7565b0381875afa9081156118995783916001600160401b03916101405191611a0c575b505116106119f757602092815192610a5c858561378b565b610140518452610a8f6060610a703061494a565b85518093819263021c373760e31b8352610100519033600485016144c7565b0381865afa90811561172157610abc92916001600160401b039161014051916119c8575b50511690614974565b90610ac9610100516149e8565b835192610ad6858561378b565b60018452601f1985013688860137610aed84613dc2565b5283519060a08201908282106001600160401b038311176119ae57908552338252610140518783019081528583019182526060830194855260808301969096528451633635205760e01b81523060048201526024810186905282516001600160a01b03166044820152955163ffffffff1660648701525160a0608487015285929190610b7d9060e4850190613de3565b93518385036043190160a485015280518086526101405195890195918901915b89828210611997575050505082610bca608082930151956043198382030160c48401526101405196614278565b039161014051905af191821561198a576101405192611942575b50610bee3061494a565b81516316a26f7b60e11b815281516001600160a01b0316600482015260209091015163ffffffff1660248201529160448301819052610100516001600160a01b03908116606485015261014051859185916084918391907f0000000000000000000000000000000000000000000000000000000000000000165af1928315611899576101405193611913575b5082156118fe578151632495a59960e01b8152610100518590829060049082906001600160a01b03165afa9081156114125761014051916118e1575b506101408051845163a9059cbb60e01b90915230600452602486905290516001600160a01b0392909216929091869060448180875af160016101405151148116156118bf575b828552156118a65761014051825285820185905283820152610100516001600160a01b03169033907f3c011840976564fcc4559f75e69f8a0a69ebb3a374148ab6358c306c33dd41fa90606090a48051632495a59960e01b815260a0518490829060049082906001600160a01b03165afa8015611899576101405160c05261187a575b50610d8e6024356004016139d7565b8151906318160ddd60e01b8252848260048160018060a01b0360c051165afa9081156114125785906101405192611849575b8451632e35e48960e11b815260ff909116600482015230602482015292508280604481015b038160018060a01b0360e051165afa91821561141257610140519261181a575b50600161012081905281019081106118065761012051820180921161159457610e3c91610e3291856148ac565b61014051906139ca565b610140516080528151632495a59960e01b815260e0518590829060049082906001600160a01b03165afa9081156114125761014051916117e9575b5060e051610e909085906001600160a01b038416614413565b610e9e6024356004016139d7565b906024356044810135903603604219018112156101bd5760ff855193610ec385613755565b1683528587840152846003198260243501360301126101bd57845190610ee882613726565b600481602435010135906001600160401b0382116101bd57366023602435830184010112156101bd57602491610f2e9036908435840101600481810135918d01016137c7565b835281350101358782015284830152610f4b6024356004016139d7565b60ff855191630bdf607b60e41b8352166004820152868160248160018060a01b0360e051165afa9081156117dc5790879161014051916117af575b5061172e578451631fb96acd60e01b8152919282908190610faa906004830161429c565b03816101405160018060a01b0360e051165af18015611721576116e2575b50610fe790610140516080525b60e051906001600160a01b031661439b565b60c0516001600160a01b03163b156101bd5781516340c10f1960e01b8152610140805130600484015260248301849052905160c05183916044918391906001600160a01b03165af18015611412576116c7575b508151634fb3ccc560e01b815260e0518590829060049082906001600160a01b03165afa90811561141257610140519161169a575b506001600160a01b031680611632575b50815161108c858261378b565b610140518152825161109e848261378b565b61012051815285610140515b601f198601811061161157505083516110c281613726565b610100516001600160a01b03168152670de0b6b3a7640000878201526110e782613dc2565b526110f181613dc2565b508351916110ff858461378b565b61012051835286610140515b601f19870181106115f0575050845161112381613726565b338152848882015261113484613dc2565b5261113e83613dc2565b508451639d45c28160e01b815287816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156115e35761014051916115ae575b5061119e63ffffffff8216426139f8565b80600119810111611594576111bf63ffffffff9182841690600119016139e5565b168651946111cd888761378b565b61012051865289610140515b601f198a0181106115505750509063ffffffff92918851956111fa87613770565b865260c0516001600160a01b03168b87015288860152606085015216608083015260a082015261122982613dc2565b5261123381613dc2565b5060c05161126f9083906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169116614413565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b156101bd57825190634e5cd2fd60e11b825260448201306004840152846024840152815180915260648301908760648260051b86010193019161014051905b82821061141f5750506101405192849250829003905081837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015611412576113f7575b5060c05160ff9390611366906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911661439b565b6113746024356004016139d7565b9280519560018060a01b036101005116875260018060a01b0360a0511690870152850152606084015260805160808401521660a082015260018060a01b0360e05116907ffaf66257bc51b727241377e1a40ac926a99e5955fa479671239fd77ebb72082560c03392a3610120515f516020615f355f395f51905f52556101405180f35b610140516114049161378b565b610140516101bd5784611328565b83513d61014051823e3d90fd5b90919293899060631987820301835285519060c081019180519260c0835283518091528460e0840194019061014051905b8d8183106115125750505050808401516001600160a01b0316828501528a8101518284038c8401528051808552610140519486019491860191905b8d8183106114e25750505050916114d09160a08463ffffffff60608897015116606084015263ffffffff608082015116608084015201519060a0818403910152614278565b950191019161012051019092916112d9565b9092969193949582885160018060a01b0381511683520151838201520195019161012051018e959493929161148b565b909296919394956bffffffffffffffffffffffff83895160018060a01b038151168452015116838201520195019161012051018e9594939291611450565b895161155b81613770565b60608152610140518382015260608b820152610140516060820152610140516080820152606060a082015282828a010152018a906111d9565b634e487b7160e01b61014051526011600452602461014051fd5b90508781813d83116115dc575b6115c5818361378b565b810103126101bd576115d690614462565b8861118d565b503d6115bb565b86513d61014051823e3d90fd5b86516115fb81613726565b5f81525f8382015282828701015201879061110b565b855161161c81613726565b5f81525f838201528282850101520186906110aa565b803b156101bd57825163569ebf3560e11b815260e0516001600160a01b03166004820152336024820152604481018590526101405190918290606490829084905af18015611412571561107f576101405161168c9161378b565b610140516101bd578461107f565b6116ba9150853d87116116c0575b6116b2818361378b565b81019061394d565b8561106f565b503d6116a8565b610140516116d49161378b565b610140516101bd578461103a565b8581813d831161171a575b6116f7818361378b565b810103126101bd57519065ffffffffffff821682036101bd579050610fe7610fc8565b503d6116ed565b84513d61014051823e3d90fd5b84516355ef454960e11b8152928390819061174c906004830161429c565b03816101405160018060a01b0360e051165af1801561172157610140519061177c575b610fe79250608052610fd5565b508582813d83116117a8575b611792818361378b565b810103126117a457610fe7915161176f565b5f80fd5b503d611788565b6117cf9150823d84116117d5575b6117c7818361378b565b81019061396c565b88610f86565b503d6117bd565b85513d61014051823e3d90fd5b6118009150853d87116116c0576116b2818361378b565b85610e77565b634e487b7160e01b5f52601160045260245ffd5b9091508481813d8311611842575b611832818361378b565b810103126117a457519085610e05565b503d611828565b915082813d8311611873575b61185f818361378b565b810103126117a45784610de5925191610dc0565b503d611855565b61189090843d86116116c0576116b2818361378b565b60c05283610d7f565b82513d61014051823e3d90fd5b82635274afe760e01b6101405152600452602461014051fd5b60018115166118d557833b15153d151616610cfc565b823d61014051823e3d90fd5b6118f89150853d87116116c0576116b2818361378b565b85610cb6565b639671a89f60e01b6101405152600461014051fd5b9092508381813d831161193b575b61192b818361378b565b810103126117a457519184610c7a565b503d611921565b9091503d8061014051833e611957818361378b565b81019082818303126101bd57805191848201516001600160401b0381116101bd5761198292016144ff565b509083610be4565b50513d61014051823e3d90fd5b835188529687019689965090920191600101610b9d565b634e487b7160e01b61014051526041600452602461014051fd5b6119ea915060603d6060116119f0575b6119e2818361378b565b810190614473565b88610ab3565b503d6119d8565b6337cb51dd60e21b6101405152600461014051fd5b611a25915060603d6060116119f0576119e2818361378b565b86610a44565b63125a110760e21b6101405152600461014051fd5b9050846109e5565b6109da91935091611a6f6001600160401b039360603d6060116119f0576119e2818361378b565b939150916109c3565b90503d8061014051833e611a8c818361378b565b8101906020818303126101bd578051906001600160401b0382116101bd57019080601f830112156101bd578151611ac281613dab565b92611acf8651948561378b565b81845260208085019260051b820101918383116101bd5760208201905b838210611b0057505050505061097c61096f565b81516001600160401b0381116101bd57602091611b22878480948801016144ff565b815201910190611aec565b63617ab12d60e11b6101405152600461014051fd5b90506020813d602011611b6c575b81611b5d6020938361378b565b810103126117a45751836108bc565b3d9150611b50565b6040513d61014051823e3d90fd5b631caa993f60e31b6101405152600461014051fd5b611bb0915060203d6020116117d5576117c7818361378b565b8461087e565b631fb1705560e21b6101405152600461014051fd5b611be4915060203d6020116117d5576117c7818361378b565b8461082b565b632f20e08d60e21b6101405152600461014051fd5b6315f1e15d60e21b6101405152600461014051fd5b506020813d602011611c4b575b81611c2e6020938361378b565b810103126101bd575161ffff811681036101bd5761ffff90610758565b3d9150611c21565b63162908e360e11b6101405152600461014051fd5b63722fdba960e01b6101405152600461014051fd5b6303fa1eaf60e41b6101405152600461014051fd5b611cab915060203d6020116117d5576117c7818361378b565b816106b3565b346101bd5760403660031901126101bd57611cca61369d565b602435611cd561418d565b611cdd6141c5565b8015611c5357611cec82613a8c565b60ff611cf7836138dd565b5416906001600160a01b03611d0b84613ac7565b16604051632495a59960e01b8152602081600481855afa908115611b7457611d4a918491610140519161203d575b506001600160a01b03168686613ae4565b928315611c5357604051632495a59960e01b8152602081600481865afa908115611b7457610140519161201e575b506040516370a0823160e01b81523360048201526001600160a01b039190911690602081602481855afa8015611b745785916101405191611fe9575b5010611fd4576101405150604051906323b872dd60e01b610140515233600452306024528460445260206101405160646101405161014051855af16001610140515114811615611fbe575b826040526101405160605215611fa657803b156101bd57632770a7eb60e21b82526101408051306004850152602484018790529051909183916044918391905af18015611b7457611f8b575b5060405192611e5984613726565b81845260208085018681526040516328c0104d60e21b8152955160ff166004870152516024860152610140516001600160a01b03881695919082906044908290895af18015611b7457611f59575b50611ee886611eb760409861386d565b611ec28882546139ca565b9055611ecd336137fd565b865f52602052875f20611ee18882546139ca565b9055613835565b611ef38682546139ca565b905585519283526020830152838583015261014051606083015260808201527fb70df16af75ac670505ba4838560ffb3b81c9e17013cf90d28a49ba89aa92bbf60a03392a360015f516020615f355f395f51905f52558151906101405182526020820152f35b6020813d602011611f83575b81611f726020938361378b565b810103126117a45750611ee8611ea7565b3d9150611f65565b61014051611f989161378b565b610140516101bd5785611e4b565b635274afe760e01b6101405152600452602461014051fd5b60018115166118d557813b15153d151616611dff565b631e9acf1760e31b6101405152600461014051fd5b9150506020813d602011612016575b816120056020938361378b565b810103126117a45784905188611db4565b3d9150611ff8565b612037915060203d6020116116c0576116b2818361378b565b86611d78565b612056915060203d6020116116c0576116b2818361378b565b87611d39565b346101bd5760603660031901126101bd5761207561369d565b61207d6136b3565b906044356001600160401b0381116101bd57366023820112156101bd576120ae9036906024816004013591016137c7565b9161014051549160ff8360081c1615928380946125bf575b80156125a8575b1561254c5783600160ff198316176101405155612537575b5060ff610140515460081c16916120fb83614218565b6001600160a01b0382169283158015612526575b611c535761213a9261212361213592614218565b61212c81613f6c565b61213583613f6c565b61400d565b610140516101405152606560205260406101405120815f5260205260ff60405f205416156124da575b5f516020615f155f395f51905f526101405152606560205260406101405120815f5260205260ff60405f20541615612479575b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b156101bd576040519063a982182160e01b82523060048301526040602483015281806121f861014051936044830190614278565b038161014051875af18015611b745761245e575b50604051916020919061221f838561378b565b6101405184525f3681376040519361223685613726565b61014051855283850152604093845191612250868461378b565b60018352601f1986019185610140515b84811061243957505061227284613dc2565b5261227c83613dc2565b5085519161228a878461378b565b6001835236868401373061229d83613dc2565b52803b156101bd57918551928391630caa1e7960e21b83526064830130600485015260606024850152815180915260848401908860848260051b87010193019161014051905b8a8c8484106123f4575050505050508261230e81926003198382030160448401526101405196613de3565b039161014051905af18015611721576123d9575b5082516114b9808201908282106001600160401b038311176119ae5784918391614a1c833930815203019061014051f080156114125760018060a01b03166bffffffffffffffffffffffff60a01b5f516020615ef55f395f51905f525416175f516020615ef55f395f51905f525561239b576101405180f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989161ff001961014051541661014051555160018152a180806102d7565b610140516123e69161378b565b610140516101bd5783612322565b6001939597985081929496612428916083198d8303018752838a5163ffffffff815116845201519181858201520190613de3565b9601920192018795949391926122e3565b885161244481613726565b610140518152606083820152828288010152018690612260565b6101405161246b9161378b565b610140516101bd578261220c565b5f516020615f155f395f51905f526101405152606560205260406101405120815f5260205260405f20600160ff1982541617905533905f516020615f155f395f51905f525f516020615ed55f395f51905f526101405161014051a482612196565b610140516101405152606560205260406101405120815f5260205260405f20600160ff198254161790553381610140515f516020615ed55f395f51905f526101405161014051a4612163565b506001600160a01b0382161561210f565b6101019061ffff1916176101405155846120e5565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b1580156120cd5750600160ff8216146120cd565b50600160ff8216106120c6565b346101bd57610140513660031901126101bd5760405163099b4ac360e31b8152602090f35b346117a45760603660031901126117a45761260a61369d565b6044356001600160401b0381116117a4576126299036906004016136f9565b61263161418d565b6126396141c5565b61264283613a8c565b60ff61264d846138dd565b541661265884613ac7565b9261266285613be6565b92909461266e336137fd565b60018060a01b0388165f5260205260405f2054958615612a795761269188613835565b5480158015612a70575b612a2d57604051631344d22360e21b8152306004820152976001600160a01b038a16969060208a6024818b5afa998a1561299c575f9a612a3c575b506126e08b61386d565b54996126ee8b828685613a16565b9a8b15612a2d5781156129a757893b156117a457604051636b7f4ea560e11b8152915f83600481838f5af192831561299c5761272f93612987575b506139ca565b808b11611c535760209b602460018060a01b0387169d8e604051928380926370a0823160e01b82523060048301525afa908115611b74576101405191612955575b508c11611c53576020946127ce92612789908e9061420b565b938181111561294657505061014051925b83158061293d575b612933575b6127b08261386d565b556127ba336137fd565b8a5f52855260405f20610140519055613835565b556001600160a01b0316946127e286613d9b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169960e49061281b8b8d87614413565b896040519a8b9485936319744d6760e11b85528b600486015260248501528d6044850152336064850152602435608485015260c060a48501528160c48501528484013761014051838284010152601f80199101168101030181610140518c5af1958615611b745761014051966128fe575b506040976128999161439b565b865192835260018060a01b03166020830152848683015283606083015260808201527f08ff66ecb49dae546cb0e627ae91149487467cce83755083bb0e1ad9ac59315160a03392a360015f516020615f355f395f51905f525582519182526020820152f35b9095506020813d60201161292b575b8161291a6020938361378b565b810103126117a4575194604061288c565b3d915061290d565b50610140516127a7565b508015156127a2565b61294f9161420b565b9261279a565b90506020813d60201161297f575b816129706020938361378b565b810103126117a457518e612770565b3d9150612963565b5f6129919161378b565b5f610140528e612729565b6040513d5f823e3d90fd5b905080156117a4576040516370a0823160e01b8152306004820152906020826024816001600160a01b038a165afa91821561299c575f926129f9575b50808210156129f2575061272f565b905061272f565b9091506020813d602011612a25575b81612a156020938361378b565b810103126117a45751908d6129e3565b3d9150612a08565b63162908e360e11b5f5260045ffd5b9099506020813d602011612a68575b81612a586020938361378b565b810103126117a45751988b6126d6565b3d9150612a4b565b5087811061269b565b63487192dd60e01b5f5260045ffd5b346117a45760203660031901126117a4576004356001600160e01b031981168082036117a457612ab781613d57565b805f525f516020615f555f395f51905f5260205260ff60405f205416612b3a575f9081525f516020615f555f395f51905f526020908152604091829020805460ff1916600117905581516001600160e01b031993909316835233908301527f95c3658c5e0c74e20cf12db371b9b67d26e97a1937f6d2284f88cc44d036b4f691a1005b63722fdba960e01b5f5260045ffd5b346117a45760203660031901126117a45760206001600160a01b03612b74612b6f61369d565b613915565b5416604051908152f35b346117a45760403660031901126117a457612b976136b3565b336001600160a01b03821603612bb557612bb39060043561410c565b005b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b346117a4575f3660031901126117a4576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346117a45760603660031901126117a457612c6f61369d565b612c776136b3565b906044356001600160401b0381116117a4577ff8aaad08ee23b49c9bb44e3bca6c7efa43442fc4281245a7f2475aa2632718d191612cbc612cc49236906004016136c9565b9290946140ae565b612cdc60405192839260018060a01b03169583613984565b0390a2005b346117a45760403660031901126117a457600435612cfd6136b3565b90612d176102cd825f526065602052600160405f20015490565b805f52606560205260405f2060018060a01b0383165f5260205260ff60405f20541615612d4057005b5f8181526065602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291905f516020615ed55f395f51905f529080a4005b346117a45760203660031901126117a4576020612db26004355f526065602052600160405f20015490565b604051908152f35b346117a45760403660031901126117a457612dd361369d565b612de4612dde6136b3565b916137fd565b60018060a01b0382165f5260205260405f2054612e0082613835565b54604051631344d22360e21b815230600482015292906020846024816001600160a01b0386165afa90811561299c575f91612e87575b608094508315159283612e5e575b506040519384526020840152604083015215156060820152f35b909250811590811591612e74575b509185612e44565b612e7e915061386d565b54151585612e6c565b90506020843d602011612eb4575b81612ea26020938361378b565b810103126117a4576080935190612e36565b3d9150612e95565b346117a45760603660031901126117a45760043560ff8116908181036117a457612ee46136b3565b916044356001600160a01b038116918282036117a457612f02613c5d565b604051630642194560e41b81526001600160a01b0386811660048301819052929190602090829060249082907f0000000000000000000000000000000000000000000000000000000000000000165afa90811561299c575f916133ff575b50156133f0576001600160a01b03612f77876138a5565b54166133e157612f8684613d9b565b604051632495a59960e01b8152602081600481885afa90811561299c575f916133c2575b50604051632495a59960e01b81526001600160a01b03919091169390602081600481875afa801561299c5785915f916133a3575b506001600160a01b031603613394575f516020615ef55f395f51905f52546040516306fdde0360e01b8152906001600160a01b03165f82600481885afa91821561299c575f92613378575b506040516306fdde0360e01b8152915f836004818a5afa92831561299c575f9361335c575b506040516395d89b4160e01b8152925f846004818b5afa93841561299c575f9461332c575b50906130828161308893614345565b93614345565b9160405163313ce56760e01b81526020816004818b5afa90811561299c575f916132e6575b50916130e25f6130f4959360209560ff6040519889978896879563e8a0aed360e01b8752606060048801526064870190614278565b85810360031901602487015290614278565b9116604483015203925af190811561299c575f916132c7575b5060405163581dfd6560e01b81526001600160a01b03918216600482018190527f000000000000000000000000000000000000000000000000000000000000000090921698919691906020816024818d5afa90811561299c575f916132a8575b506001600160a01b038116156132285760209950916131e97f2840f76158e444c1d697cef892b963f173b7dd782e0e1887a5521b40f9841b079594926131ff94955b6131b8826138a5565b80546001600160a01b0319166001600160a01b0389161790556131da826138dd565b9060ff19825416179055613915565b80546001600160a01b0319169091179055613e1f565b6040805160ff9790971687526001600160a01b03919091166020870181905295a4604051908152f35b50929160205f996024604051809c8193636b9b622960e01b83528c60048401525af191821561299c576131e97f2840f76158e444c1d697cef892b963f173b7dd782e0e1887a5521b40f9841b07956131ff9460209c5f9161328b575b50956131af565b6132a291508d3d8f116116c0576116b2818361378b565b8d613284565b6132c1915060203d6020116116c0576116b2818361378b565b8a61316d565b6132e0915060203d6020116116c0576116b2818361378b565b8861310d565b90506020939193813d602011613324575b816133046020938361378b565b810103126117a457519160ff831683036117a457909290916130e26130ad565b3d91506132f7565b8261308893929550613352613082923d805f833e61334a818361378b565b8101906142e3565b9592509250613073565b6133719193503d805f833e61334a818361378b565b918a61304e565b61338d9192503d805f833e61334a818361378b565b9089613029565b632711b74d60e11b5f5260045ffd5b6133bc915060203d6020116116c0576116b2818361378b565b89612fde565b6133db915060203d6020116116c0576116b2818361378b565b87612faa565b63abd5ec5d60e01b5f5260045ffd5b630681d31960e51b5f5260045ffd5b613418915060203d6020116117d5576117c7818361378b565b87612f60565b346117a45760403660031901126117a457600461343961369d565b61344281613a8c565b61344b81613ac7565b602060ff613458846138dd565b54604051632495a59960e01b81529591169285919082906001600160a01b03165afa92831561299c576060936134af926134a9925f926134ce575b50602435916001600160a01b0316908590613ae4565b91613be6565b604080519384526001600160a01b039182166020850152911690820152f35b6134e891925060203d6020116116c0576116b2818361378b565b9086613493565b346117a4575f3660031901126117a45760206040515f516020615f155f395f51905f528152f35b346117a4575f3660031901126117a4576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346117a45760203660031901126117a45760206001600160a01b03612b7461358061369d565b6138a5565b346117a45760403660031901126117a45761359e61369d565b6135a9612dde6136b3565b60018060a01b0382165f5260205260405f20546135c582613835565b54604051631344d22360e21b815230600482015290926020826024816001600160a01b0385165afa91821561299c575f92613613575b6020612db285858861360c8761386d565b5492613a16565b9291506020833d602011613642575b8161362f6020938361378b565b810103126117a4579151909160206135fb565b3d9150613622565b346117a45760203660031901126117a4576004359063ffffffff60e01b82168092036117a457602091637965db0b60e01b811490811561368c575b5015158152f35b6301ffc9a760e01b14905083613685565b600435906001600160a01b03821682036117a457565b602435906001600160a01b03821682036117a457565b9181601f840112156117a4578235916001600160401b0383116117a4576020808501948460051b0101116117a457565b9181601f840112156117a4578235916001600160401b0383116117a457602083818601950101116117a457565b604081019081106001600160401b0382111761374157604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761374157604052565b60c081019081106001600160401b0382111761374157604052565b90601f801991011681019081106001600160401b0382111761374157604052565b6001600160401b03811161374157601f01601f191660200190565b9291926137d3826137ac565b916137e1604051938461378b565b8294818452818301116117a4578281602093845f960137010152565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041056020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041066020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041076020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041016020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041036020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041026020526040902090565b908160209103126117a457516001600160a01b03811681036117a45790565b908160209103126117a4575180151581036117a45790565b60208082528101839052604001915f905b8082106139a25750505090565b90919283359063ffffffff82168092036117a457602081600193829352019401920190613995565b9190820180921161180657565b3560ff811681036117a45790565b8181029291811591840414171561180657565b8115613a02570490565b634e487b7160e01b5f52601260045260245ffd5b90928115613a8457613a2881846139ca565b9081158015613a7c575b613a7357613a4985613a4484866139e5565b6139f8565b9315613a6b5750925b03613a5b575090565b9190808311613a675750565b9150565b905092613a52565b50505050505f90565b508415613a32565b505050505f90565b6001600160a01b03613a9d826138a5565b541615613aa75750565b636315b1a360e01b5f9081526001600160a01b0391909116600452602490fd5b6001600160a01b0390613ad9906138a5565b541690811561339457565b604051632e35e48960e11b815260ff9190911660048201523060248201529190602090839060449082906001600160a01b03165afa91821561299c575f92613bb1575b506040516318160ddd60e01b81529190602090839060049082906001600160a01b03165afa91821561299c575f92613b7d575b5060018101809111611806576001820180921161180657613b7a926148ac565b90565b9091506020813d602011613ba9575b81613b996020938361378b565b810103126117a45751905f613b5a565b3d9150613b8c565b91506020823d602011613bde575b81613bcc6020938361378b565b810103126117a4579051906020613b27565b3d9150613bbf565b6001600160a01b03613bf782613915565b541690811561339457604051632495a59960e01b815290602090829060049082906001600160a01b03165afa90811561299c575f91613c3e575b506001600160a01b031691565b613c57915060203d6020116116c0576116b2818361378b565b5f613c31565b335f9081527f077c9007150f571447365d3bd382bfccc7ce27caa9a880faf975f29f5cafcb79602052604090205460ff1615613c9557565b613d536020613d336011613ca833614585565b603784613cc15f516020615f155f395f51905f5261466b565b6040519687947f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000828701528051918291018587015e8401907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e01015f838201520301601f19810183528261378b565b60405162461bcd60e51b8152602060048201529182916024830190614278565b0390fd5b5f81815260656020908152604080832033845290915290205460ff1615613d7b5750565b6020613d336011613d5393603784613cc1613d9533614585565b9361466b565b6001600160a01b03161561339457565b6001600160401b0381116137415760051b60200190565b805115613dcf5760200190565b634e487b7160e01b5f52603260045260245ffd5b90602080835192838152019201905f5b818110613e005750505090565b82516001600160a01b0316845260209384019390920191600101613df3565b6001600160a01b03165f8181527f9bc6362768ab7acc6d678440e313110dcbcf1f3bce0a546779127863e2b88a09602052604081205490919060ff16613f68576040918251613e6e848261378b565b60018152601f19840136602083013782613e8782613dc2565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156117a457613ef05f929183928751948580948193630287f75160e51b8352306004840152836024840152606060448401526064830190613de3565b03925af18015613f5e5790849291613f49575b508080527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db604104602052209060018060a01b03165f526020525f20600160ff19825416179055565b613f5692505f915061378b565b815f5f613f03565b84513d5f823e3d90fd5b5050565b6001600160a01b0381165f9081527f714e2b715aba5871ce4d05fd99bd94a36ff2fc47fdbc62c5c1bc08b10a64682e602052604090205460ff1615613fae5750565b6001600160a01b03165f8181527f714e2b715aba5871ce4d05fd99bd94a36ff2fc47fdbc62c5c1bc08b10a64682e60205260408120805460ff1916600117905533919063099b4ac360e31b905f516020615ed55f395f51905f529080a4565b6001600160a01b0381165f9081527f9a824e0a361d4dc38210d8d46718f5a024fcd5426c72be95b98894eaa9f84758602052604090205460ff161561404f5750565b6001600160a01b03165f8181527f9a824e0a361d4dc38210d8d46718f5a024fcd5426c72be95b98894eaa9f8475860205260408120805460ff191660011790553391906340e1896760e01b905f516020615ed55f395f51905f529080a4565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614801591906140f8575b506140ea57565b6282b42960e81b5f5260045ffd5b6001600160a01b031630141590505f6140e3565b805f52606560205260405f2060018060a01b0383165f5260205260ff60405f205416614136575050565b5f8181526065602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4565b60025f516020615f355f395f51905f5254146141b65760025f516020615f355f395f51905f5255565b633ee5aeb560e01b5f5260045ffd5b6340e1896760e01b5f525f516020615f555f395f51905f526020527f3663686bcf2d729d4855c41e87932887789108353c8318329b1f901b652c7a495460ff16612b3a57565b9190820391821161180657565b1561421f57565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b906040906020835260ff815116602084015260208101518284015201519060608082015260a060206142da84516040608086015260c0850190614278565b93015191015290565b6020818303126117a4578051906001600160401b0382116117a4570181601f820112156117a457805190614316826137ac565b92614324604051948561378b565b828452602083830101116117a457815f9260208093018386015e8301015290565b6001614399916022602080969560405197889461185b60f21b828701528051918291018587015e840190602d60f81b84830152805192839101602383015e01015f838201520301601f19810184528361378b565b565b906143a75f828461470d565b156143b0575050565b6143ba818361475a565b156143f2575f6143ca91836147be565b156143d25750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b50635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b919061442082828561470d565b1561442a57505050565b614434818461475a565b1561444457906143ca91836147be565b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b519063ffffffff821682036117a457565b908160609103126117a4576040519061448b82613755565b80516001600160401b03811681036117a4578252602081015180600f0b81036117a45760208301526144bf90604001614462565b604082015290565b6001600160a01b039182168152825182166020808301919091529092015163ffffffff16604083015291909116606082015260800190565b9080601f830112156117a457815161451681613dab565b92614524604051948561378b565b81845260208085019260051b8201019283116117a457602001905b82821061454c5750505090565b815181526020918201910161453f565b6040519061456982613726565b5f6020838281520152565b908151811015613dcf570160200190565b61458f602a6137ac565b9061459d604051928361378b565b602a82526145ab602a6137ac565b6020830190601f1901368237825115613dcf5760309053815160011015613dcf576078602183015360295b6001811161462a57506145e65790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b90600f81166010811015613dcf576f181899199a1a9b1b9c1cb0b131b232b360811b901a6146588385614574565b5360041c908015611806575f19016145d6565b61467560426137ac565b90614683604051928361378b565b6042825261469160426137ac565b6020830190601f1901368237825115613dcf5760309053815160011015613dcf576078602183015360415b600181116146cc57506145e65790565b90600f81166010811015613dcf576f181899199a1a9b1b9c1cb0b131b232b360811b901a6146fa8385614574565b5360041c908015611806575f19016146bc565b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f5114841615614749575b50604052565b3d15903b151516909216915f614743565b60405163095ea7b360e01b5f9081526001600160a01b03909316600452602483905290929160209060448180875af19260015f511484161561479c5750604052565b600184929415166147b5573b15153d151616915f614743565b833d5f823e3d90fd5b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f511484161561479c5750604052565b5f19670de0b6b3a7640000820991670de0b6b3a76400008202918280851094039380850394146148a0578382111561488857670de0b6b3a7640000829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b5090613b7a92506139f8565b90915f19838309928083029283808610950394808603951461493d57848311156149255790829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b82634e487b715f52156003026011186020526024601cfd5b505090613b7a92506139f8565b61495261455c565b506040519061496082613726565b6001600160a01b031681525f602082015290565b6001600160401b03821690811580156149d7575b6149d0576149ac926001600160401b039183838216115f146149c957505b166147f9565b670de0b6b3a7640000811115613b7a5750670de0b6b3a764000090565b90506149a6565b5050505f90565b506001600160401b03811615614988565b604051906149f760408361378b565b6001825260203681840137614a0b82613dc2565b6001600160a01b0390911690529056fe60803460b857601f6114b938819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36113e890816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f3560e01c806319f37361146104d75780632a5c792a146103f7578063715018a6146103a057806378a89567146103775780638da5cb5b14610350578063e4b50cb8146102f3578063e8a0aed3146100fe5763f2fde38b14610071575f80fd5b346100fa5760203660031901126100fa576004356001600160a01b038116908190036100fa5761009f610622565b80156100e7575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b346100fa5760603660031901126100fa5760043567ffffffffffffffff81116100fa5761012f903690600401610564565b60243567ffffffffffffffff81116100fa5761014f903690600401610564565b60443560ff81168091036100fa57610165610622565b8251156102e4578151156102e45760405190610d4a9081830183811067ffffffffffffffff8211176102c55783926106498439608081526101ba6101ac60808301886105fe565b8281036020840152866105fe565b91604082015260603391015203905ff080156102d95760018060a01b0316905f5160206113935f395f51905f5254680100000000000000008110156102c5577f6596c1670eb3390048d23721809c3da5d3f531375ac0e2cab0f77a808ed643316102ac60209561023d8460018896015f5160206113935f395f51905f52556105ba565b81546001600160a01b0360039290921b82811b199091169087901b1790915584165f9081527f5c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b90160205260409020600160ff198254161790556102ba6040519283926040845260408401906105fe565b8281038984015233966105fe565b0390a3604051908152f35b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b63ecd7b0d160e01b5f5260045ffd5b346100fa5760203660031901126100fa576004355f5160206113935f395f51905f5254811015610341576103286020916105ba565b905460405160039290921b1c6001600160a01b03168152f35b634e23d03560e01b5f5260045ffd5b346100fa575f3660031901126100fa575f546040516001600160a01b039091168152602090f35b346100fa575f3660031901126100fa5760205f5160206113935f395f51905f5254604051908152f35b346100fa575f3660031901126100fa576103b8610622565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100fa575f3660031901126100fa576040518060205f5160206113935f395f51905f5254928381520180925f5160206113935f395f51905f525f527f941c1986f49ec6ae940eac60cb4772d232cf98b87e67237c4199544bd2b0123d905f5b8181106104b8575050508161046d910382610542565b604051918291602083019060208452518091526040830191905f5b818110610496575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610488565b82546001600160a01b0316845260209093019260019283019201610457565b346100fa5760203660031901126100fa576004356001600160a01b03811681036100fa576001600160a01b03165f9081527f5c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b9016020908152604090912060ff9054166040519015158152f35b90601f8019910116810190811067ffffffffffffffff8211176102c557604052565b81601f820112156100fa5780359067ffffffffffffffff82116102c55760405192610599601f8401601f191660200185610542565b828452602083830101116100fa57815f926020809301838601378301015290565b5f5160206113935f395f51905f52548110156105ea575f5160206113935f395f51905f525f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b5f546001600160a01b0316330361063557565b63118cdaa760e01b5f523360045260245ffdfe60a0604052346103a757610d4a80380380610019816103ab565b9283398101906080818303126103a75780516001600160401b0381116103a757826100459183016103d0565b602082015190926001600160401b0382116103a7576100659183016103d0565b9060408101519060ff821682036103a757606001516001600160a01b03811691908290036103a75783516001600160401b0381116102b857600354600181811c9116801561039d575b602082101461029a57601f811161033a575b50602094601f82116001146102d7579481929394955f926102cc575b50508160011b915f199060031b1c1916176003555b82516001600160401b0381116102b857600454600181811c911680156102ae575b602082101461029a57601f8111610237575b506020601f82116001146101d457819293945f926101c9575b50508160011b915f199060031b1c1916176004555b81156101b657600580546001600160a01b03198116841790915560405192906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360805261092890816104228239608051816105860152f35b631e4fbdf760e01b5f525f60045260245ffd5b015190505f8061013d565b601f1982169060045f52805f20915f5b81811061021f57509583600195969710610207575b505050811b01600455610152565b01515f1960f88460031b161c191690555f80806101f9565b9192602060018192868b0151815501940192016101e4565b60045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c81019160208410610290575b601f0160051c01905b8181106102855750610124565b5f8155600101610278565b909150819061026f565b634e487b7160e01b5f52602260045260245ffd5b90607f1690610112565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100dc565b601f1982169560035f52805f20915f5b8881106103225750836001959697981061030a575b505050811b016003556100f1565b01515f1960f88460031b161c191690555f80806102fc565b919260206001819286850151815501940192016102e7565b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f830160051c81019160208410610393575b601f0160051c01905b81811061038857506100c0565b5f815560010161037b565b9091508190610372565b90607f16906100ae565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176102b857604052565b81601f820112156103a7578051906001600160401b0382116102b8576103ff601f8301601f19166020016103ab565b92828452602083830101116103a757815f9260208093018386015e830101529056fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde031461072857508063095ea7b3146106a657806318160ddd1461068957806323b872dd146105aa578063313ce5671461056d57806340c10f191461048857806370a0823114610451578063715018a6146103f65780638da5cb5b146103ce57806395d89b41146102b35780639dc29fac146101c1578063a9059cbb14610190578063dd62ed3e146101405763f2fde38b146100b6575f80fd5b3461013c57602036600319011261013c576100cf610821565b6100d76108cb565b6001600160a01b0316801561012957600580546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b3461013c57604036600319011261013c57610159610821565b610161610837565b6001600160a01b039182165f908152600160209081526040808320949093168252928352819020549051908152f35b3461013c57604036600319011261013c576101b66101ac610821565b602435903361084d565b602060405160018152f35b3461013c57604036600319011261013c576101da610821565b6024359062aa36a746036102a6575b6001600160a01b031690811561029357815f525f60205260405f205481811061027a57817f8e396d76882ec446db350fff179713730a08f97b6f7ed47a2d265e25d1be97df92602092855f525f84520360405f205580600254036002555f847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051858152a3604051908152a2005b8263391434e360e21b5f5260045260245260445260645ffd5b634b637e8f60e11b5f525f60045260245ffd5b6102ae6108cb565b6101e9565b3461013c575f36600319011261013c576040515f6004548060011c906001811680156103c4575b6020831081146103b057828552908115610394575060011461033f575b50819003601f01601f191681019067ffffffffffffffff82118183101761032b57610327829182604052826107f7565b0390f35b634e487b7160e01b5f52604160045260245ffd5b905060045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5f905b82821061037e575060209150820101826102f7565b6001816020925483858801015201910190610369565b90506020925060ff191682840152151560051b820101826102f7565b634e487b7160e01b5f52602260045260245ffd5b91607f16916102da565b3461013c575f36600319011261013c576005546040516001600160a01b039091168152602090f35b3461013c575f36600319011261013c5761040e6108cb565b600580546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461013c57602036600319011261013c576001600160a01b03610472610821565b165f525f602052602060405f2054604051908152f35b3461013c57604036600319011261013c576104a1610821565b6024359062aa36a74603610560575b6001600160a01b031690811561054d57600254818101809111610539577ff1d77337c6a221f354c5cb0cb43e4cb10ed777c3be30497bfc625e280ceaae6991602091600255835f525f825260405f20818154019055835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051858152a3604051908152a2005b634e487b7160e01b5f52601160045260245ffd5b63ec442f0560e01b5f525f60045260245ffd5b6105686108cb565b6104b0565b3461013c575f36600319011261013c57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461013c57606036600319011261013c576105c3610821565b6105cb610837565b6001600160a01b0382165f818152600160209081526040808320338452909152902054909260443592915f198110610609575b506101b6935061084d565b83811061066e57841561065b573315610648576101b6945f52600160205260405f2060018060a01b0333165f526020528360405f2091039055846105fe565b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b8390637dc7a0d960e11b5f523360045260245260445260645ffd5b3461013c575f36600319011261013c576020600254604051908152f35b3461013c57604036600319011261013c576106bf610821565b60243590331561065b576001600160a01b031690811561064857335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461013c575f36600319011261013c575f6003548060011c906001811680156107ed575b6020831081146103b05782855290811561039457506001146107985750819003601f01601f191681019067ffffffffffffffff82118183101761032b57610327829182604052826107f7565b905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5f905b8282106107d7575060209150820101826102f7565b60018160209254838588010152019101906107c2565b91607f169161074c565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361013c57565b602435906001600160a01b038216820361013c57565b6001600160a01b0316908115610293576001600160a01b031691821561054d57815f525f60205260405f205481811061027a57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092855f525f84520360405f2055845f525f825260405f20818154019055604051908152a3565b6005546001600160a01b031633036108df57565b63118cdaa760e01b5f523360045260245ffdfea2646970667358221220b01ff437261f6cb2053cee001897173d6ec615564db68650687e059d06b6e85c64736f6c634300081e00335c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b900a2646970667358221220c6ba4ad719ad846d0f942f83fdbb0030855d3fc7f16efed5aa679ee68a7ab0d864736f6c634300081e00332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041006270edb7c868f86fda4adedba75108201087268ea345934db8bad688e1feb91b9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f8131bc7f7376b7d8d09601d142eed8304a0dce3fa238f43875ec194c35e4400a2646970667358221220ce2da5b17cd79d831d46f2767954350f0a32b356e59274731e8bb80baf9c67b364736f6c634300081e0033000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc3900000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda0000000000000000000000007a9aa939261fb51ddc9491c929224417a84592290000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f647

Deployed Bytecode

0x610160806040526004361015610013575f80fd5b5f610140525f3560e01c90816301ffc9a71461364a57508063049d4c1b14613585578063084727b11461355a578063103f290714613516578063117803e3146134ef57806313ce83c61461341e57806317f24c0f14612ebc5780631ac71d9614612dba578063248a9ca314612d875780632f2ff15d14612ce1578063303ca95614612c5657806331232bc914612c1257806336568abe14612b7e57806338b1b34e14612b495780633aa83ec714612a885780633ab22ff2146125f157806344260ff4146125cc5780634571e3a61461205c5780634cef3daf14611cb157806357b53c8f1461061957806371e2c264146105d257806391d14854146105835780639fc2c4761461053c578063a217fddf1461051c578063ae501345146104d5578063aed753c914610496578063b526578714610467578063bac1e94b1461038d578063c63fd502146102de578063d547741f14610294578063d5c1c20a1461024d578063efd96978146101fb578063f4c3066f146101c45763f5beff1014610198575f80fd5b346101bd57610140513660031901126101bd576040516340e1896760e01b8152602090f35b6101405180fd5b346101bd57610140513660031901126101bd575f516020615ef55f395f51905f52546040516001600160a01b039091168152602090f35b346101bd5760203660031901126101bd5760043563ffffffff60e01b81168091036101bd5761014051525f516020615f555f395f51905f52602052602060ff6040610140512054166040519015158152f35b346101bd57610140513660031901126101bd576040517f00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a6001600160a01b03168152602090f35b346101bd5760403660031901126101bd576102d76004356102b36136b3565b906102d26102cd825f526065602052600160405f20015490565b613d57565b61410c565b6101405180f35b346101bd5760803660031901126101bd576102f761369d565b6102ff6136b3565b6044356001600160401b0381116101bd5761031e9036906004016136c9565b90926064356001600160401b0381116101bd577f9efdc3d07eb312e06bf36ea85db02aec96817d7c7421f919027b240eaf34035d9361036461036b9236906004016136f9565b50506140ae565b61038360405192839260018060a01b03169583613984565b0390a26101405180f35b346101bd5760203660031901126101bd576004356001600160e01b031981168082036101bd576103bc81613d57565b8061014051525f516020615f555f395f51905f5260205260ff60406101405120541615610452576101408051919091525f516020615f555f395f51905f5260209081529051604090819020805460ff1916905580516001600160e01b031993909316835233918301919091527fc7e56e17b0a6c4b467df6495e1eda1baecd7ba20604e80c1058ac06f4578d85e91a16101405180f35b6368c87f3360e11b6101405152600461014051fd5b346101bd5760203660031901126101bd57602061048261369d565b6040516001600160a01b0390911630148152f35b346101bd5760403660031901126101bd576104bf6104b261369d565b6104ba613c5d565b61386d565b6104cc60243582546139ca565b90556101405180f35b346101bd57610140513660031901126101bd576040517f0000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f6476001600160a01b03168152602090f35b346101bd57610140513660031901126101bd576020604051610140518152f35b346101bd57610140513660031901126101bd576040517f000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a6001600160a01b03168152602090f35b346101bd5760403660031901126101bd5761059c6136b3565b60043561014051526065602052604061014051209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346101bd57610140513660031901126101bd576040517f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda6001600160a01b03168152602090f35b346101bd5760403660031901126101bd5761063261369d565b60e0526001600160401b03602435116101bd576060602435360360031901126101bd5761065d61418d565b6040516336b87bd760e11b81523360048201526020816024817f00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a6001600160a01b03165afa908115611b74576101405191611c92575b5015611c7d5763099b4ac360e31b61014051525f516020615f555f395f51905f5260205260ff604061014051205416611c68576106f160e051613a8c565b6024803501358015611c535760405163e063f81f60e01b81523360048201523060248201526020816044817f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda6001600160a01b03165afa8015611b74576101405190611c14575b61ffff915016611bff5760ff61076f60e0516138dd565b541660ff6107816024356004016139d7565b1603611bea5761079260e051613be6565b61010052506107a260e051613ac7565b60a0526040516107b181613726565b30815261014051602082019081526040516304c1b8eb60e31b815282516001600160a01b039081166004830152915163ffffffff1660248201527f000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc399091169290602081604481875afa908115611b74576101405191611bcb575b5015611bb6576040516333869dd160e11b815233600482015282516001600160a01b03166024820152602083015163ffffffff166044820152602081606481875afa908115611b74576101405191611b97575b5015611b8257604051906338f6b94760e21b8252600482015260208160248160018060a01b0361010051165afa908115611b74576101405191611b42575b508015611b2d5760409061095982516108d5848261378b565b60018152601f198401366020830137336108ee82613dc2565b526109436108fe610100516149e8565b85516315a29d4d60e11b815287516001600160a01b03166004820152602088015163ffffffff1660248201529390928492839291608060448501526084840190613de3565b8281036003190160648401526101405194613de3565b0381885afa908115611412576101405191611a78575b5061097c61098291613dc2565b51613dc2565b518181106119f75782519163021c373760e31b8352606083806109ad610100518933600485016144c7565b0381895afa928315611721576101405193611a48575b506001600160401b03916109da91838551166148ac565b915116808211611a40575b506001600160401b038111611a2b5760606001600160401b03610a2392169383518093819263021c373760e31b8352610100519033600485016144c7565b0381875afa9081156118995783916001600160401b03916101405191611a0c575b505116106119f757602092815192610a5c858561378b565b610140518452610a8f6060610a703061494a565b85518093819263021c373760e31b8352610100519033600485016144c7565b0381865afa90811561172157610abc92916001600160401b039161014051916119c8575b50511690614974565b90610ac9610100516149e8565b835192610ad6858561378b565b60018452601f1985013688860137610aed84613dc2565b5283519060a08201908282106001600160401b038311176119ae57908552338252610140518783019081528583019182526060830194855260808301969096528451633635205760e01b81523060048201526024810186905282516001600160a01b03166044820152955163ffffffff1660648701525160a0608487015285929190610b7d9060e4850190613de3565b93518385036043190160a485015280518086526101405195890195918901915b89828210611997575050505082610bca608082930151956043198382030160c48401526101405196614278565b039161014051905af191821561198a576101405192611942575b50610bee3061494a565b81516316a26f7b60e11b815281516001600160a01b0316600482015260209091015163ffffffff1660248201529160448301819052610100516001600160a01b03908116606485015261014051859185916084918391907f000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a165af1928315611899576101405193611913575b5082156118fe578151632495a59960e01b8152610100518590829060049082906001600160a01b03165afa9081156114125761014051916118e1575b506101408051845163a9059cbb60e01b90915230600452602486905290516001600160a01b0392909216929091869060448180875af160016101405151148116156118bf575b828552156118a65761014051825285820185905283820152610100516001600160a01b03169033907f3c011840976564fcc4559f75e69f8a0a69ebb3a374148ab6358c306c33dd41fa90606090a48051632495a59960e01b815260a0518490829060049082906001600160a01b03165afa8015611899576101405160c05261187a575b50610d8e6024356004016139d7565b8151906318160ddd60e01b8252848260048160018060a01b0360c051165afa9081156114125785906101405192611849575b8451632e35e48960e11b815260ff909116600482015230602482015292508280604481015b038160018060a01b0360e051165afa91821561141257610140519261181a575b50600161012081905281019081106118065761012051820180921161159457610e3c91610e3291856148ac565b61014051906139ca565b610140516080528151632495a59960e01b815260e0518590829060049082906001600160a01b03165afa9081156114125761014051916117e9575b5060e051610e909085906001600160a01b038416614413565b610e9e6024356004016139d7565b906024356044810135903603604219018112156101bd5760ff855193610ec385613755565b1683528587840152846003198260243501360301126101bd57845190610ee882613726565b600481602435010135906001600160401b0382116101bd57366023602435830184010112156101bd57602491610f2e9036908435840101600481810135918d01016137c7565b835281350101358782015284830152610f4b6024356004016139d7565b60ff855191630bdf607b60e41b8352166004820152868160248160018060a01b0360e051165afa9081156117dc5790879161014051916117af575b5061172e578451631fb96acd60e01b8152919282908190610faa906004830161429c565b03816101405160018060a01b0360e051165af18015611721576116e2575b50610fe790610140516080525b60e051906001600160a01b031661439b565b60c0516001600160a01b03163b156101bd5781516340c10f1960e01b8152610140805130600484015260248301849052905160c05183916044918391906001600160a01b03165af18015611412576116c7575b508151634fb3ccc560e01b815260e0518590829060049082906001600160a01b03165afa90811561141257610140519161169a575b506001600160a01b031680611632575b50815161108c858261378b565b610140518152825161109e848261378b565b61012051815285610140515b601f198601811061161157505083516110c281613726565b610100516001600160a01b03168152670de0b6b3a7640000878201526110e782613dc2565b526110f181613dc2565b508351916110ff858461378b565b61012051835286610140515b601f19870181106115f0575050845161112381613726565b338152848882015261113484613dc2565b5261113e83613dc2565b508451639d45c28160e01b815287816004817f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda6001600160a01b03165afa9081156115e35761014051916115ae575b5061119e63ffffffff8216426139f8565b80600119810111611594576111bf63ffffffff9182841690600119016139e5565b168651946111cd888761378b565b61012051865289610140515b601f198a0181106115505750509063ffffffff92918851956111fa87613770565b865260c0516001600160a01b03168b87015288860152606085015216608083015260a082015261122982613dc2565b5261123381613dc2565b5060c05161126f9083906001600160a01b037f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda81169116614413565b7f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda6001600160a01b03163b156101bd57825190634e5cd2fd60e11b825260448201306004840152846024840152815180915260648301908760648260051b86010193019161014051905b82821061141f5750506101405192849250829003905081837f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda6001600160a01b03165af18015611412576113f7575b5060c05160ff9390611366906001600160a01b037f0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda8116911661439b565b6113746024356004016139d7565b9280519560018060a01b036101005116875260018060a01b0360a0511690870152850152606084015260805160808401521660a082015260018060a01b0360e05116907ffaf66257bc51b727241377e1a40ac926a99e5955fa479671239fd77ebb72082560c03392a3610120515f516020615f355f395f51905f52556101405180f35b610140516114049161378b565b610140516101bd5784611328565b83513d61014051823e3d90fd5b90919293899060631987820301835285519060c081019180519260c0835283518091528460e0840194019061014051905b8d8183106115125750505050808401516001600160a01b0316828501528a8101518284038c8401528051808552610140519486019491860191905b8d8183106114e25750505050916114d09160a08463ffffffff60608897015116606084015263ffffffff608082015116608084015201519060a0818403910152614278565b950191019161012051019092916112d9565b9092969193949582885160018060a01b0381511683520151838201520195019161012051018e959493929161148b565b909296919394956bffffffffffffffffffffffff83895160018060a01b038151168452015116838201520195019161012051018e9594939291611450565b895161155b81613770565b60608152610140518382015260608b820152610140516060820152610140516080820152606060a082015282828a010152018a906111d9565b634e487b7160e01b61014051526011600452602461014051fd5b90508781813d83116115dc575b6115c5818361378b565b810103126101bd576115d690614462565b8861118d565b503d6115bb565b86513d61014051823e3d90fd5b86516115fb81613726565b5f81525f8382015282828701015201879061110b565b855161161c81613726565b5f81525f838201528282850101520186906110aa565b803b156101bd57825163569ebf3560e11b815260e0516001600160a01b03166004820152336024820152604481018590526101405190918290606490829084905af18015611412571561107f576101405161168c9161378b565b610140516101bd578461107f565b6116ba9150853d87116116c0575b6116b2818361378b565b81019061394d565b8561106f565b503d6116a8565b610140516116d49161378b565b610140516101bd578461103a565b8581813d831161171a575b6116f7818361378b565b810103126101bd57519065ffffffffffff821682036101bd579050610fe7610fc8565b503d6116ed565b84513d61014051823e3d90fd5b84516355ef454960e11b8152928390819061174c906004830161429c565b03816101405160018060a01b0360e051165af1801561172157610140519061177c575b610fe79250608052610fd5565b508582813d83116117a8575b611792818361378b565b810103126117a457610fe7915161176f565b5f80fd5b503d611788565b6117cf9150823d84116117d5575b6117c7818361378b565b81019061396c565b88610f86565b503d6117bd565b85513d61014051823e3d90fd5b6118009150853d87116116c0576116b2818361378b565b85610e77565b634e487b7160e01b5f52601160045260245ffd5b9091508481813d8311611842575b611832818361378b565b810103126117a457519085610e05565b503d611828565b915082813d8311611873575b61185f818361378b565b810103126117a45784610de5925191610dc0565b503d611855565b61189090843d86116116c0576116b2818361378b565b60c05283610d7f565b82513d61014051823e3d90fd5b82635274afe760e01b6101405152600452602461014051fd5b60018115166118d557833b15153d151616610cfc565b823d61014051823e3d90fd5b6118f89150853d87116116c0576116b2818361378b565b85610cb6565b639671a89f60e01b6101405152600461014051fd5b9092508381813d831161193b575b61192b818361378b565b810103126117a457519184610c7a565b503d611921565b9091503d8061014051833e611957818361378b565b81019082818303126101bd57805191848201516001600160401b0381116101bd5761198292016144ff565b509083610be4565b50513d61014051823e3d90fd5b835188529687019689965090920191600101610b9d565b634e487b7160e01b61014051526041600452602461014051fd5b6119ea915060603d6060116119f0575b6119e2818361378b565b810190614473565b88610ab3565b503d6119d8565b6337cb51dd60e21b6101405152600461014051fd5b611a25915060603d6060116119f0576119e2818361378b565b86610a44565b63125a110760e21b6101405152600461014051fd5b9050846109e5565b6109da91935091611a6f6001600160401b039360603d6060116119f0576119e2818361378b565b939150916109c3565b90503d8061014051833e611a8c818361378b565b8101906020818303126101bd578051906001600160401b0382116101bd57019080601f830112156101bd578151611ac281613dab565b92611acf8651948561378b565b81845260208085019260051b820101918383116101bd5760208201905b838210611b0057505050505061097c61096f565b81516001600160401b0381116101bd57602091611b22878480948801016144ff565b815201910190611aec565b63617ab12d60e11b6101405152600461014051fd5b90506020813d602011611b6c575b81611b5d6020938361378b565b810103126117a45751836108bc565b3d9150611b50565b6040513d61014051823e3d90fd5b631caa993f60e31b6101405152600461014051fd5b611bb0915060203d6020116117d5576117c7818361378b565b8461087e565b631fb1705560e21b6101405152600461014051fd5b611be4915060203d6020116117d5576117c7818361378b565b8461082b565b632f20e08d60e21b6101405152600461014051fd5b6315f1e15d60e21b6101405152600461014051fd5b506020813d602011611c4b575b81611c2e6020938361378b565b810103126101bd575161ffff811681036101bd5761ffff90610758565b3d9150611c21565b63162908e360e11b6101405152600461014051fd5b63722fdba960e01b6101405152600461014051fd5b6303fa1eaf60e41b6101405152600461014051fd5b611cab915060203d6020116117d5576117c7818361378b565b816106b3565b346101bd5760403660031901126101bd57611cca61369d565b602435611cd561418d565b611cdd6141c5565b8015611c5357611cec82613a8c565b60ff611cf7836138dd565b5416906001600160a01b03611d0b84613ac7565b16604051632495a59960e01b8152602081600481855afa908115611b7457611d4a918491610140519161203d575b506001600160a01b03168686613ae4565b928315611c5357604051632495a59960e01b8152602081600481865afa908115611b7457610140519161201e575b506040516370a0823160e01b81523360048201526001600160a01b039190911690602081602481855afa8015611b745785916101405191611fe9575b5010611fd4576101405150604051906323b872dd60e01b610140515233600452306024528460445260206101405160646101405161014051855af16001610140515114811615611fbe575b826040526101405160605215611fa657803b156101bd57632770a7eb60e21b82526101408051306004850152602484018790529051909183916044918391905af18015611b7457611f8b575b5060405192611e5984613726565b81845260208085018681526040516328c0104d60e21b8152955160ff166004870152516024860152610140516001600160a01b03881695919082906044908290895af18015611b7457611f59575b50611ee886611eb760409861386d565b611ec28882546139ca565b9055611ecd336137fd565b865f52602052875f20611ee18882546139ca565b9055613835565b611ef38682546139ca565b905585519283526020830152838583015261014051606083015260808201527fb70df16af75ac670505ba4838560ffb3b81c9e17013cf90d28a49ba89aa92bbf60a03392a360015f516020615f355f395f51905f52558151906101405182526020820152f35b6020813d602011611f83575b81611f726020938361378b565b810103126117a45750611ee8611ea7565b3d9150611f65565b61014051611f989161378b565b610140516101bd5785611e4b565b635274afe760e01b6101405152600452602461014051fd5b60018115166118d557813b15153d151616611dff565b631e9acf1760e31b6101405152600461014051fd5b9150506020813d602011612016575b816120056020938361378b565b810103126117a45784905188611db4565b3d9150611ff8565b612037915060203d6020116116c0576116b2818361378b565b86611d78565b612056915060203d6020116116c0576116b2818361378b565b87611d39565b346101bd5760603660031901126101bd5761207561369d565b61207d6136b3565b906044356001600160401b0381116101bd57366023820112156101bd576120ae9036906024816004013591016137c7565b9161014051549160ff8360081c1615928380946125bf575b80156125a8575b1561254c5783600160ff198316176101405155612537575b5060ff610140515460081c16916120fb83614218565b6001600160a01b0382169283158015612526575b611c535761213a9261212361213592614218565b61212c81613f6c565b61213583613f6c565b61400d565b610140516101405152606560205260406101405120815f5260205260ff60405f205416156124da575b5f516020615f155f395f51905f526101405152606560205260406101405120815f5260205260ff60405f20541615612479575b507f000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc396001600160a01b031691823b156101bd576040519063a982182160e01b82523060048301526040602483015281806121f861014051936044830190614278565b038161014051875af18015611b745761245e575b50604051916020919061221f838561378b565b6101405184525f3681376040519361223685613726565b61014051855283850152604093845191612250868461378b565b60018352601f1986019185610140515b84811061243957505061227284613dc2565b5261227c83613dc2565b5085519161228a878461378b565b6001835236868401373061229d83613dc2565b52803b156101bd57918551928391630caa1e7960e21b83526064830130600485015260606024850152815180915260848401908860848260051b87010193019161014051905b8a8c8484106123f4575050505050508261230e81926003198382030160448401526101405196613de3565b039161014051905af18015611721576123d9575b5082516114b9808201908282106001600160401b038311176119ae5784918391614a1c833930815203019061014051f080156114125760018060a01b03166bffffffffffffffffffffffff60a01b5f516020615ef55f395f51905f525416175f516020615ef55f395f51905f525561239b576101405180f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989161ff001961014051541661014051555160018152a180806102d7565b610140516123e69161378b565b610140516101bd5783612322565b6001939597985081929496612428916083198d8303018752838a5163ffffffff815116845201519181858201520190613de3565b9601920192018795949391926122e3565b885161244481613726565b610140518152606083820152828288010152018690612260565b6101405161246b9161378b565b610140516101bd578261220c565b5f516020615f155f395f51905f526101405152606560205260406101405120815f5260205260405f20600160ff1982541617905533905f516020615f155f395f51905f525f516020615ed55f395f51905f526101405161014051a482612196565b610140516101405152606560205260406101405120815f5260205260405f20600160ff198254161790553381610140515f516020615ed55f395f51905f526101405161014051a4612163565b506001600160a01b0382161561210f565b6101019061ffff1916176101405155846120e5565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b1580156120cd5750600160ff8216146120cd565b50600160ff8216106120c6565b346101bd57610140513660031901126101bd5760405163099b4ac360e31b8152602090f35b346117a45760603660031901126117a45761260a61369d565b6044356001600160401b0381116117a4576126299036906004016136f9565b61263161418d565b6126396141c5565b61264283613a8c565b60ff61264d846138dd565b541661265884613ac7565b9261266285613be6565b92909461266e336137fd565b60018060a01b0388165f5260205260405f2054958615612a795761269188613835565b5480158015612a70575b612a2d57604051631344d22360e21b8152306004820152976001600160a01b038a16969060208a6024818b5afa998a1561299c575f9a612a3c575b506126e08b61386d565b54996126ee8b828685613a16565b9a8b15612a2d5781156129a757893b156117a457604051636b7f4ea560e11b8152915f83600481838f5af192831561299c5761272f93612987575b506139ca565b808b11611c535760209b602460018060a01b0387169d8e604051928380926370a0823160e01b82523060048301525afa908115611b74576101405191612955575b508c11611c53576020946127ce92612789908e9061420b565b938181111561294657505061014051925b83158061293d575b612933575b6127b08261386d565b556127ba336137fd565b8a5f52855260405f20610140519055613835565b556001600160a01b0316946127e286613d9b565b7f000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a6001600160a01b03169960e49061281b8b8d87614413565b896040519a8b9485936319744d6760e11b85528b600486015260248501528d6044850152336064850152602435608485015260c060a48501528160c48501528484013761014051838284010152601f80199101168101030181610140518c5af1958615611b745761014051966128fe575b506040976128999161439b565b865192835260018060a01b03166020830152848683015283606083015260808201527f08ff66ecb49dae546cb0e627ae91149487467cce83755083bb0e1ad9ac59315160a03392a360015f516020615f355f395f51905f525582519182526020820152f35b9095506020813d60201161292b575b8161291a6020938361378b565b810103126117a4575194604061288c565b3d915061290d565b50610140516127a7565b508015156127a2565b61294f9161420b565b9261279a565b90506020813d60201161297f575b816129706020938361378b565b810103126117a457518e612770565b3d9150612963565b5f6129919161378b565b5f610140528e612729565b6040513d5f823e3d90fd5b905080156117a4576040516370a0823160e01b8152306004820152906020826024816001600160a01b038a165afa91821561299c575f926129f9575b50808210156129f2575061272f565b905061272f565b9091506020813d602011612a25575b81612a156020938361378b565b810103126117a45751908d6129e3565b3d9150612a08565b63162908e360e11b5f5260045ffd5b9099506020813d602011612a68575b81612a586020938361378b565b810103126117a45751988b6126d6565b3d9150612a4b565b5087811061269b565b63487192dd60e01b5f5260045ffd5b346117a45760203660031901126117a4576004356001600160e01b031981168082036117a457612ab781613d57565b805f525f516020615f555f395f51905f5260205260ff60405f205416612b3a575f9081525f516020615f555f395f51905f526020908152604091829020805460ff1916600117905581516001600160e01b031993909316835233908301527f95c3658c5e0c74e20cf12db371b9b67d26e97a1937f6d2284f88cc44d036b4f691a1005b63722fdba960e01b5f5260045ffd5b346117a45760203660031901126117a45760206001600160a01b03612b74612b6f61369d565b613915565b5416604051908152f35b346117a45760403660031901126117a457612b976136b3565b336001600160a01b03821603612bb557612bb39060043561410c565b005b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b346117a4575f3660031901126117a4576040517f000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc396001600160a01b03168152602090f35b346117a45760603660031901126117a457612c6f61369d565b612c776136b3565b906044356001600160401b0381116117a4577ff8aaad08ee23b49c9bb44e3bca6c7efa43442fc4281245a7f2475aa2632718d191612cbc612cc49236906004016136c9565b9290946140ae565b612cdc60405192839260018060a01b03169583613984565b0390a2005b346117a45760403660031901126117a457600435612cfd6136b3565b90612d176102cd825f526065602052600160405f20015490565b805f52606560205260405f2060018060a01b0383165f5260205260ff60405f20541615612d4057005b5f8181526065602090815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291905f516020615ed55f395f51905f529080a4005b346117a45760203660031901126117a4576020612db26004355f526065602052600160405f20015490565b604051908152f35b346117a45760403660031901126117a457612dd361369d565b612de4612dde6136b3565b916137fd565b60018060a01b0382165f5260205260405f2054612e0082613835565b54604051631344d22360e21b815230600482015292906020846024816001600160a01b0386165afa90811561299c575f91612e87575b608094508315159283612e5e575b506040519384526020840152604083015215156060820152f35b909250811590811591612e74575b509185612e44565b612e7e915061386d565b54151585612e6c565b90506020843d602011612eb4575b81612ea26020938361378b565b810103126117a4576080935190612e36565b3d9150612e95565b346117a45760603660031901126117a45760043560ff8116908181036117a457612ee46136b3565b916044356001600160a01b038116918282036117a457612f02613c5d565b604051630642194560e41b81526001600160a01b0386811660048301819052929190602090829060249082907f0000000000000000000000007a9aa939261fb51ddc9491c929224417a8459229165afa90811561299c575f916133ff575b50156133f0576001600160a01b03612f77876138a5565b54166133e157612f8684613d9b565b604051632495a59960e01b8152602081600481885afa90811561299c575f916133c2575b50604051632495a59960e01b81526001600160a01b03919091169390602081600481875afa801561299c5785915f916133a3575b506001600160a01b031603613394575f516020615ef55f395f51905f52546040516306fdde0360e01b8152906001600160a01b03165f82600481885afa91821561299c575f92613378575b506040516306fdde0360e01b8152915f836004818a5afa92831561299c575f9361335c575b506040516395d89b4160e01b8152925f846004818b5afa93841561299c575f9461332c575b50906130828161308893614345565b93614345565b9160405163313ce56760e01b81526020816004818b5afa90811561299c575f916132e6575b50916130e25f6130f4959360209560ff6040519889978896879563e8a0aed360e01b8752606060048801526064870190614278565b85810360031901602487015290614278565b9116604483015203925af190811561299c575f916132c7575b5060405163581dfd6560e01b81526001600160a01b03918216600482018190527f0000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f64790921698919691906020816024818d5afa90811561299c575f916132a8575b506001600160a01b038116156132285760209950916131e97f2840f76158e444c1d697cef892b963f173b7dd782e0e1887a5521b40f9841b079594926131ff94955b6131b8826138a5565b80546001600160a01b0319166001600160a01b0389161790556131da826138dd565b9060ff19825416179055613915565b80546001600160a01b0319169091179055613e1f565b6040805160ff9790971687526001600160a01b03919091166020870181905295a4604051908152f35b50929160205f996024604051809c8193636b9b622960e01b83528c60048401525af191821561299c576131e97f2840f76158e444c1d697cef892b963f173b7dd782e0e1887a5521b40f9841b07956131ff9460209c5f9161328b575b50956131af565b6132a291508d3d8f116116c0576116b2818361378b565b8d613284565b6132c1915060203d6020116116c0576116b2818361378b565b8a61316d565b6132e0915060203d6020116116c0576116b2818361378b565b8861310d565b90506020939193813d602011613324575b816133046020938361378b565b810103126117a457519160ff831683036117a457909290916130e26130ad565b3d91506132f7565b8261308893929550613352613082923d805f833e61334a818361378b565b8101906142e3565b9592509250613073565b6133719193503d805f833e61334a818361378b565b918a61304e565b61338d9192503d805f833e61334a818361378b565b9089613029565b632711b74d60e11b5f5260045ffd5b6133bc915060203d6020116116c0576116b2818361378b565b89612fde565b6133db915060203d6020116116c0576116b2818361378b565b87612faa565b63abd5ec5d60e01b5f5260045ffd5b630681d31960e51b5f5260045ffd5b613418915060203d6020116117d5576117c7818361378b565b87612f60565b346117a45760403660031901126117a457600461343961369d565b61344281613a8c565b61344b81613ac7565b602060ff613458846138dd565b54604051632495a59960e01b81529591169285919082906001600160a01b03165afa92831561299c576060936134af926134a9925f926134ce575b50602435916001600160a01b0316908590613ae4565b91613be6565b604080519384526001600160a01b039182166020850152911690820152f35b6134e891925060203d6020116116c0576116b2818361378b565b9086613493565b346117a4575f3660031901126117a45760206040515f516020615f155f395f51905f528152f35b346117a4575f3660031901126117a4576040517f0000000000000000000000007a9aa939261fb51ddc9491c929224417a84592296001600160a01b03168152602090f35b346117a45760203660031901126117a45760206001600160a01b03612b7461358061369d565b6138a5565b346117a45760403660031901126117a45761359e61369d565b6135a9612dde6136b3565b60018060a01b0382165f5260205260405f20546135c582613835565b54604051631344d22360e21b815230600482015290926020826024816001600160a01b0385165afa91821561299c575f92613613575b6020612db285858861360c8761386d565b5492613a16565b9291506020833d602011613642575b8161362f6020938361378b565b810103126117a4579151909160206135fb565b3d9150613622565b346117a45760203660031901126117a4576004359063ffffffff60e01b82168092036117a457602091637965db0b60e01b811490811561368c575b5015158152f35b6301ffc9a760e01b14905083613685565b600435906001600160a01b03821682036117a457565b602435906001600160a01b03821682036117a457565b9181601f840112156117a4578235916001600160401b0383116117a4576020808501948460051b0101116117a457565b9181601f840112156117a4578235916001600160401b0383116117a457602083818601950101116117a457565b604081019081106001600160401b0382111761374157604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761374157604052565b60c081019081106001600160401b0382111761374157604052565b90601f801991011681019081106001600160401b0382111761374157604052565b6001600160401b03811161374157601f01601f191660200190565b9291926137d3826137ac565b916137e1604051938461378b565b8294818452818301116117a4578281602093845f960137010152565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041056020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041066020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041076020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041016020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041036020526040902090565b6001600160a01b03165f9081527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041026020526040902090565b908160209103126117a457516001600160a01b03811681036117a45790565b908160209103126117a4575180151581036117a45790565b60208082528101839052604001915f905b8082106139a25750505090565b90919283359063ffffffff82168092036117a457602081600193829352019401920190613995565b9190820180921161180657565b3560ff811681036117a45790565b8181029291811591840414171561180657565b8115613a02570490565b634e487b7160e01b5f52601260045260245ffd5b90928115613a8457613a2881846139ca565b9081158015613a7c575b613a7357613a4985613a4484866139e5565b6139f8565b9315613a6b5750925b03613a5b575090565b9190808311613a675750565b9150565b905092613a52565b50505050505f90565b508415613a32565b505050505f90565b6001600160a01b03613a9d826138a5565b541615613aa75750565b636315b1a360e01b5f9081526001600160a01b0391909116600452602490fd5b6001600160a01b0390613ad9906138a5565b541690811561339457565b604051632e35e48960e11b815260ff9190911660048201523060248201529190602090839060449082906001600160a01b03165afa91821561299c575f92613bb1575b506040516318160ddd60e01b81529190602090839060049082906001600160a01b03165afa91821561299c575f92613b7d575b5060018101809111611806576001820180921161180657613b7a926148ac565b90565b9091506020813d602011613ba9575b81613b996020938361378b565b810103126117a45751905f613b5a565b3d9150613b8c565b91506020823d602011613bde575b81613bcc6020938361378b565b810103126117a4579051906020613b27565b3d9150613bbf565b6001600160a01b03613bf782613915565b541690811561339457604051632495a59960e01b815290602090829060049082906001600160a01b03165afa90811561299c575f91613c3e575b506001600160a01b031691565b613c57915060203d6020116116c0576116b2818361378b565b5f613c31565b335f9081527f077c9007150f571447365d3bd382bfccc7ce27caa9a880faf975f29f5cafcb79602052604090205460ff1615613c9557565b613d536020613d336011613ca833614585565b603784613cc15f516020615f155f395f51905f5261466b565b6040519687947f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000828701528051918291018587015e8401907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e01015f838201520301601f19810183528261378b565b60405162461bcd60e51b8152602060048201529182916024830190614278565b0390fd5b5f81815260656020908152604080832033845290915290205460ff1615613d7b5750565b6020613d336011613d5393603784613cc1613d9533614585565b9361466b565b6001600160a01b03161561339457565b6001600160401b0381116137415760051b60200190565b805115613dcf5760200190565b634e487b7160e01b5f52603260045260245ffd5b90602080835192838152019201905f5b818110613e005750505090565b82516001600160a01b0316845260209384019390920191600101613df3565b6001600160a01b03165f8181527f9bc6362768ab7acc6d678440e313110dcbcf1f3bce0a546779127863e2b88a09602052604081205490919060ff16613f68576040918251613e6e848261378b565b60018152601f19840136602083013782613e8782613dc2565b527f000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc396001600160a01b0316803b156117a457613ef05f929183928751948580948193630287f75160e51b8352306004840152836024840152606060448401526064830190613de3565b03925af18015613f5e5790849291613f49575b508080527f032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db604104602052209060018060a01b03165f526020525f20600160ff19825416179055565b613f5692505f915061378b565b815f5f613f03565b84513d5f823e3d90fd5b5050565b6001600160a01b0381165f9081527f714e2b715aba5871ce4d05fd99bd94a36ff2fc47fdbc62c5c1bc08b10a64682e602052604090205460ff1615613fae5750565b6001600160a01b03165f8181527f714e2b715aba5871ce4d05fd99bd94a36ff2fc47fdbc62c5c1bc08b10a64682e60205260408120805460ff1916600117905533919063099b4ac360e31b905f516020615ed55f395f51905f529080a4565b6001600160a01b0381165f9081527f9a824e0a361d4dc38210d8d46718f5a024fcd5426c72be95b98894eaa9f84758602052604090205460ff161561404f5750565b6001600160a01b03165f8181527f9a824e0a361d4dc38210d8d46718f5a024fcd5426c72be95b98894eaa9f8475860205260408120805460ff191660011790553391906340e1896760e01b905f516020615ed55f395f51905f529080a4565b337f000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc396001600160a01b031614801591906140f8575b506140ea57565b6282b42960e81b5f5260045ffd5b6001600160a01b031630141590505f6140e3565b805f52606560205260405f2060018060a01b0383165f5260205260ff60405f205416614136575050565b5f8181526065602090815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4565b60025f516020615f355f395f51905f5254146141b65760025f516020615f355f395f51905f5255565b633ee5aeb560e01b5f5260045ffd5b6340e1896760e01b5f525f516020615f555f395f51905f526020527f3663686bcf2d729d4855c41e87932887789108353c8318329b1f901b652c7a495460ff16612b3a57565b9190820391821161180657565b1561421f57565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b906040906020835260ff815116602084015260208101518284015201519060608082015260a060206142da84516040608086015260c0850190614278565b93015191015290565b6020818303126117a4578051906001600160401b0382116117a4570181601f820112156117a457805190614316826137ac565b92614324604051948561378b565b828452602083830101116117a457815f9260208093018386015e8301015290565b6001614399916022602080969560405197889461185b60f21b828701528051918291018587015e840190602d60f81b84830152805192839101602383015e01015f838201520301601f19810184528361378b565b565b906143a75f828461470d565b156143b0575050565b6143ba818361475a565b156143f2575f6143ca91836147be565b156143d25750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b50635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b919061442082828561470d565b1561442a57505050565b614434818461475a565b1561444457906143ca91836147be565b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b519063ffffffff821682036117a457565b908160609103126117a4576040519061448b82613755565b80516001600160401b03811681036117a4578252602081015180600f0b81036117a45760208301526144bf90604001614462565b604082015290565b6001600160a01b039182168152825182166020808301919091529092015163ffffffff16604083015291909116606082015260800190565b9080601f830112156117a457815161451681613dab565b92614524604051948561378b565b81845260208085019260051b8201019283116117a457602001905b82821061454c5750505090565b815181526020918201910161453f565b6040519061456982613726565b5f6020838281520152565b908151811015613dcf570160200190565b61458f602a6137ac565b9061459d604051928361378b565b602a82526145ab602a6137ac565b6020830190601f1901368237825115613dcf5760309053815160011015613dcf576078602183015360295b6001811161462a57506145e65790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b90600f81166010811015613dcf576f181899199a1a9b1b9c1cb0b131b232b360811b901a6146588385614574565b5360041c908015611806575f19016145d6565b61467560426137ac565b90614683604051928361378b565b6042825261469160426137ac565b6020830190601f1901368237825115613dcf5760309053815160011015613dcf576078602183015360415b600181116146cc57506145e65790565b90600f81166010811015613dcf576f181899199a1a9b1b9c1cb0b131b232b360811b901a6146fa8385614574565b5360041c908015611806575f19016146bc565b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f5114841615614749575b50604052565b3d15903b151516909216915f614743565b60405163095ea7b360e01b5f9081526001600160a01b03909316600452602483905290929160209060448180875af19260015f511484161561479c5750604052565b600184929415166147b5573b15153d151616915f614743565b833d5f823e3d90fd5b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f511484161561479c5750604052565b5f19670de0b6b3a7640000820991670de0b6b3a76400008202918280851094039380850394146148a0578382111561488857670de0b6b3a7640000829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b50634e487b715f52156003026011186020526024601cfd5b5090613b7a92506139f8565b90915f19838309928083029283808610950394808603951461493d57848311156149255790829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b82634e487b715f52156003026011186020526024601cfd5b505090613b7a92506139f8565b61495261455c565b506040519061496082613726565b6001600160a01b031681525f602082015290565b6001600160401b03821690811580156149d7575b6149d0576149ac926001600160401b039183838216115f146149c957505b166147f9565b670de0b6b3a7640000811115613b7a5750670de0b6b3a764000090565b90506149a6565b5050505f90565b506001600160401b03811615614988565b604051906149f760408361378b565b6001825260203681840137614a0b82613dc2565b6001600160a01b0390911690529056fe60803460b857601f6114b938819003918201601f19168301916001600160401b0383118484101760bc5780849260209460405283398101031260b857516001600160a01b0381169081900360b857801560a5575f80546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36113e890816100d18239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f3560e01c806319f37361146104d75780632a5c792a146103f7578063715018a6146103a057806378a89567146103775780638da5cb5b14610350578063e4b50cb8146102f3578063e8a0aed3146100fe5763f2fde38b14610071575f80fd5b346100fa5760203660031901126100fa576004356001600160a01b038116908190036100fa5761009f610622565b80156100e7575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b346100fa5760603660031901126100fa5760043567ffffffffffffffff81116100fa5761012f903690600401610564565b60243567ffffffffffffffff81116100fa5761014f903690600401610564565b60443560ff81168091036100fa57610165610622565b8251156102e4578151156102e45760405190610d4a9081830183811067ffffffffffffffff8211176102c55783926106498439608081526101ba6101ac60808301886105fe565b8281036020840152866105fe565b91604082015260603391015203905ff080156102d95760018060a01b0316905f5160206113935f395f51905f5254680100000000000000008110156102c5577f6596c1670eb3390048d23721809c3da5d3f531375ac0e2cab0f77a808ed643316102ac60209561023d8460018896015f5160206113935f395f51905f52556105ba565b81546001600160a01b0360039290921b82811b199091169087901b1790915584165f9081527f5c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b90160205260409020600160ff198254161790556102ba6040519283926040845260408401906105fe565b8281038984015233966105fe565b0390a3604051908152f35b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b63ecd7b0d160e01b5f5260045ffd5b346100fa5760203660031901126100fa576004355f5160206113935f395f51905f5254811015610341576103286020916105ba565b905460405160039290921b1c6001600160a01b03168152f35b634e23d03560e01b5f5260045ffd5b346100fa575f3660031901126100fa575f546040516001600160a01b039091168152602090f35b346100fa575f3660031901126100fa5760205f5160206113935f395f51905f5254604051908152f35b346100fa575f3660031901126100fa576103b8610622565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100fa575f3660031901126100fa576040518060205f5160206113935f395f51905f5254928381520180925f5160206113935f395f51905f525f527f941c1986f49ec6ae940eac60cb4772d232cf98b87e67237c4199544bd2b0123d905f5b8181106104b8575050508161046d910382610542565b604051918291602083019060208452518091526040830191905f5b818110610496575050500390f35b82516001600160a01b0316845285945060209384019390920191600101610488565b82546001600160a01b0316845260209093019260019283019201610457565b346100fa5760203660031901126100fa576004356001600160a01b03811681036100fa576001600160a01b03165f9081527f5c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b9016020908152604090912060ff9054166040519015158152f35b90601f8019910116810190811067ffffffffffffffff8211176102c557604052565b81601f820112156100fa5780359067ffffffffffffffff82116102c55760405192610599601f8401601f191660200185610542565b828452602083830101116100fa57815f926020809301838601378301015290565b5f5160206113935f395f51905f52548110156105ea575f5160206113935f395f51905f525f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b5f546001600160a01b0316330361063557565b63118cdaa760e01b5f523360045260245ffdfe60a0604052346103a757610d4a80380380610019816103ab565b9283398101906080818303126103a75780516001600160401b0381116103a757826100459183016103d0565b602082015190926001600160401b0382116103a7576100659183016103d0565b9060408101519060ff821682036103a757606001516001600160a01b03811691908290036103a75783516001600160401b0381116102b857600354600181811c9116801561039d575b602082101461029a57601f811161033a575b50602094601f82116001146102d7579481929394955f926102cc575b50508160011b915f199060031b1c1916176003555b82516001600160401b0381116102b857600454600181811c911680156102ae575b602082101461029a57601f8111610237575b506020601f82116001146101d457819293945f926101c9575b50508160011b915f199060031b1c1916176004555b81156101b657600580546001600160a01b03198116841790915560405192906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360805261092890816104228239608051816105860152f35b631e4fbdf760e01b5f525f60045260245ffd5b015190505f8061013d565b601f1982169060045f52805f20915f5b81811061021f57509583600195969710610207575b505050811b01600455610152565b01515f1960f88460031b161c191690555f80806101f9565b9192602060018192868b0151815501940192016101e4565b60045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c81019160208410610290575b601f0160051c01905b8181106102855750610124565b5f8155600101610278565b909150819061026f565b634e487b7160e01b5f52602260045260245ffd5b90607f1690610112565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100dc565b601f1982169560035f52805f20915f5b8881106103225750836001959697981061030a575b505050811b016003556100f1565b01515f1960f88460031b161c191690555f80806102fc565b919260206001819286850151815501940192016102e7565b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f830160051c81019160208410610393575b601f0160051c01905b81811061038857506100c0565b5f815560010161037b565b9091508190610372565b90607f16906100ae565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176102b857604052565b81601f820112156103a7578051906001600160401b0382116102b8576103ff601f8301601f19166020016103ab565b92828452602083830101116103a757815f9260208093018386015e830101529056fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde031461072857508063095ea7b3146106a657806318160ddd1461068957806323b872dd146105aa578063313ce5671461056d57806340c10f191461048857806370a0823114610451578063715018a6146103f65780638da5cb5b146103ce57806395d89b41146102b35780639dc29fac146101c1578063a9059cbb14610190578063dd62ed3e146101405763f2fde38b146100b6575f80fd5b3461013c57602036600319011261013c576100cf610821565b6100d76108cb565b6001600160a01b0316801561012957600580546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b3461013c57604036600319011261013c57610159610821565b610161610837565b6001600160a01b039182165f908152600160209081526040808320949093168252928352819020549051908152f35b3461013c57604036600319011261013c576101b66101ac610821565b602435903361084d565b602060405160018152f35b3461013c57604036600319011261013c576101da610821565b6024359062aa36a746036102a6575b6001600160a01b031690811561029357815f525f60205260405f205481811061027a57817f8e396d76882ec446db350fff179713730a08f97b6f7ed47a2d265e25d1be97df92602092855f525f84520360405f205580600254036002555f847fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051858152a3604051908152a2005b8263391434e360e21b5f5260045260245260445260645ffd5b634b637e8f60e11b5f525f60045260245ffd5b6102ae6108cb565b6101e9565b3461013c575f36600319011261013c576040515f6004548060011c906001811680156103c4575b6020831081146103b057828552908115610394575060011461033f575b50819003601f01601f191681019067ffffffffffffffff82118183101761032b57610327829182604052826107f7565b0390f35b634e487b7160e01b5f52604160045260245ffd5b905060045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5f905b82821061037e575060209150820101826102f7565b6001816020925483858801015201910190610369565b90506020925060ff191682840152151560051b820101826102f7565b634e487b7160e01b5f52602260045260245ffd5b91607f16916102da565b3461013c575f36600319011261013c576005546040516001600160a01b039091168152602090f35b3461013c575f36600319011261013c5761040e6108cb565b600580546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461013c57602036600319011261013c576001600160a01b03610472610821565b165f525f602052602060405f2054604051908152f35b3461013c57604036600319011261013c576104a1610821565b6024359062aa36a74603610560575b6001600160a01b031690811561054d57600254818101809111610539577ff1d77337c6a221f354c5cb0cb43e4cb10ed777c3be30497bfc625e280ceaae6991602091600255835f525f825260405f20818154019055835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051858152a3604051908152a2005b634e487b7160e01b5f52601160045260245ffd5b63ec442f0560e01b5f525f60045260245ffd5b6105686108cb565b6104b0565b3461013c575f36600319011261013c57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461013c57606036600319011261013c576105c3610821565b6105cb610837565b6001600160a01b0382165f818152600160209081526040808320338452909152902054909260443592915f198110610609575b506101b6935061084d565b83811061066e57841561065b573315610648576101b6945f52600160205260405f2060018060a01b0333165f526020528360405f2091039055846105fe565b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b8390637dc7a0d960e11b5f523360045260245260445260645ffd5b3461013c575f36600319011261013c576020600254604051908152f35b3461013c57604036600319011261013c576106bf610821565b60243590331561065b576001600160a01b031690811561064857335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461013c575f36600319011261013c575f6003548060011c906001811680156107ed575b6020831081146103b05782855290811561039457506001146107985750819003601f01601f191681019067ffffffffffffffff82118183101761032b57610327829182604052826107f7565b905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5f905b8282106107d7575060209150820101826102f7565b60018160209254838588010152019101906107c2565b91607f169161074c565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361013c57565b602435906001600160a01b038216820361013c57565b6001600160a01b0316908115610293576001600160a01b031691821561054d57815f525f60205260405f205481811061027a57817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092855f525f84520360405f2055845f525f825260405f20818154019055604051908152a3565b6005546001600160a01b031633036108df57565b63118cdaa760e01b5f523360045260245ffdfea2646970667358221220b01ff437261f6cb2053cee001897173d6ec615564db68650687e059d06b6e85c64736f6c634300081e00335c57fc7b6bf6ceeaa106d807cc261837d3f0a0c5e72fcba25c00753c6ce2b900a2646970667358221220c6ba4ad719ad846d0f942f83fdbb0030855d3fc7f16efed5aa679ee68a7ab0d864736f6c634300081e00332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d032f19bc7820640f5c22e33241af744bedc756ef7c75496e1c948383db6041006270edb7c868f86fda4adedba75108201087268ea345934db8bad688e1feb91b9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f8131bc7f7376b7d8d09601d142eed8304a0dce3fa238f43875ec194c35e4400a2646970667358221220ce2da5b17cd79d831d46f2767954350f0a32b356e59274731e8bb80baf9c67b364736f6c634300081e0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc3900000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda0000000000000000000000007a9aa939261fb51ddc9491c929224417a84592290000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f647

-----Decoded View---------------
Arg [0] : _allocationManager (address): 0x948a420b8CC1d6BFd0B6087C2E7c344a2CD0bc39
Arg [1] : _delegationManager (address): 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A
Arg [2] : _strategyManager (address): 0x858646372CC42E1A627fcE94aa7A7033e7CF075A
Arg [3] : _rewardsCoordinator (address): 0x7750d328b314EfFa365A0402CcfD489B80B0adda
Arg [4] : _vaultFactory (address): 0x7a9aA939261fb51Ddc9491C929224417a8459229
Arg [5] : _strategyFactory (address): 0x5e4C39Ad7A3E881585e383dB9827EB4811f6F647

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000948a420b8cc1d6bfd0b6087c2e7c344a2cd0bc39
Arg [1] : 00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a
Arg [2] : 000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a
Arg [3] : 0000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda
Arg [4] : 0000000000000000000000007a9aa939261fb51ddc9491c929224417a8459229
Arg [5] : 0000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f647


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

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.