ETH Price: $2,904.96 (+0.77%)
 

Overview

ETH Balance

Scroll LogoScroll LogoScroll Logo0 ETH

ETH Value

$0.00

More Info

Private Name Tags

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:
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SettlementDispatcher

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import { IStargate, Ticket } from "../interfaces/IStargate.sol";
import { IEtherFiDataProvider } from "../interfaces/IEtherFiDataProvider.sol";
import { IBoringOnChainQueue } from "../interfaces/IBoringOnChainQueue.sol";
import { BinSponsor } from "../interfaces/ICashModule.sol";
import { MessagingFee, OFTReceipt, SendParam } from "../interfaces/IOFT.sol";
import { IL2GatewayRouter, IL2Messenger } from "../interfaces/IScrollERC20Bridge.sol";
import { UpgradeableProxy } from "../utils/UpgradeableProxy.sol";
import { Constants } from "../utils/Constants.sol";

/**
 * @title SettlementDispatcher
 * @author [email protected]
 * @notice This contract receives payments from user safes and bridges it to another chain to pay the fiat provider
 */
contract SettlementDispatcher is UpgradeableProxy, Constants {
    using SafeERC20 for IERC20;

    /**
     * @notice Struct containing destination chain information for token bridging
     * @dev Stores all data needed to bridge a token to its destination
     */
    struct DestinationData {
        /// @notice Endpoint ID of the destination chain
        uint32 destEid;
        /// @notice Recipient address on the destination chain
        address destRecipient;
        /// @notice Address of the Stargate router to use for this token
        address stargate;
        /// @notice Whether to use canonical bridge for the token
        bool useCanonicalBridge;
        /// @notice Minimum gas limit for the canonical bridge
        uint64 minGasLimit;
    }

    /**
     * @notice Role identifier for accounts permitted to bridge tokens
     */
    bytes32 public constant SETTLEMENT_DISPATCHER_BRIDGER_ROLE = keccak256("SETTLEMENT_DISPATCHER_BRIDGER_ROLE");

    /**
     * @notice Address of the Scroll ERC20 Gateway Router
     */
    address public constant GATEWAY_ROUTER = 0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79;

    /**
     * @notice Address of the Scroll ETH Messenger
     */
    address public constant ETH_MESSENGER = 0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC;

    /**
     * @notice Bin Sponsor for the settlement dispatcher
     */
    BinSponsor public immutable binSponsor;

    /**
     * @notice Instance of the EtherFiDataProvider 
     */
    IEtherFiDataProvider public immutable dataProvider;

    /// @custom:storage-location erc7201:etherfi.storage.SettlementDispatcher
    /**
     * @dev Storage struct for SettlementDispatcher (follows ERC-7201 naming convention)
     */
    struct SettlementDispatcherStorage {
        /// @notice Mapping of token addresses to their destination chain information
        mapping(address token => DestinationData) destinationData;
        /// @notice Mapping of liquid token address to its withdraw queue
        mapping (address liquidToken => address boringQueue) liquidWithdrawQueue;
    }

    /**
     * @notice Storage location for SettlementDispatcher (ERC-7201 compliant)
     * @dev keccak256(abi.encode(uint256(keccak256("etherfi.storage.SettlementDispatcher")) - 1)) & ~bytes32(uint256(0xff))
     */
    bytes32 private constant SettlementDispatcherStorageLocation = 0x78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00;

    /**
     * @notice Emitted when destination data is set for tokens
     * @param tokens Array of token addresses that were configured
     * @param destDatas Array of destination data corresponding to each token
     */
    event DestinationDataSet(address[] tokens, DestinationData[] destDatas);
    
    /**
     * @notice Emitted when funds are successfully bridged via Stargate
     * @param token Address of the token that was bridged
     * @param amount Amount of tokens that were bridged
     * @param ticket Stargate ticket containing details of the bridge transaction
     */
    event FundsBridgedWithStargate(address indexed token, uint256 amount, Ticket ticket);
    
    /**
     * @notice Emitted when funds are withdrawn from the contract
     * @param token Address of the token that was withdrawn (address(0) for ETH)
     * @param amount Amount of tokens or ETH that was withdrawn
     * @param recipient Address that received the withdrawn funds
     */
    event FundsWithdrawn(address indexed token, uint256 amount, address indexed recipient);

    /**
     * @notice Emitted when liquid asset withdraw config is set
     * @param token Address of the liquid asset
     * @param boringQueue Address of the boring queue
     */
    event LiquidWithdrawQueueSet(address indexed token, address indexed boringQueue);
    
    /**
     * @notice Emitted when liquid asset withdrawal is requested
     * @param liquidToken Address of the liquid asset
     * @param assetOut Address of the asset out
     * @param amountToWithdraw Amount of liquid assets withdrawn
     * @param amountOut Amount of underlying tokens to receive
     */
    event LiquidWithdrawalRequested(address indexed liquidToken, address indexed assetOut, uint128 amountToWithdraw, uint128 amountOut);

    /**
     * @notice Emitted when funds are transferred to the refund wallet
     * @param asset Address of the asset transferred
     * @param refundWallet Address of the refund wallet
     * @param amount Amount of the asset transferred
     */
    event TransferToRefundWallet(address asset, address refundWallet, uint256 amount);

    /**
     * @notice Emitted when tokens are withdrawn from scroll with the canonical bridge
     * @param token Address of the token that was withdrawn
     * @param recipient Address of the recipient
     * @param amount Amount of the token that was withdrawn
     */
    event CanonicalBridgeWithdraw(address indexed token, address indexed recipient, uint256 amount);

    /**
     * @notice Thrown when input arrays have different lengths
     */
    error ArrayLengthMismatch();
    
    /**
     * @notice Thrown when an invalid address or value is provided
     */
    error InvalidValue();
    
    /**
     * @notice Thrown when trying to bridge a token with no destination data set
     */
    error DestinationDataNotSet();
    
    /**
     * @notice Thrown when the provided Stargate router does not support the token
     */
    error StargateValueInvalid();
    
    /**
     * @notice Thrown when the contract has insufficient token balance for an operation
     */
    error InsufficientBalance();
    
    /**
     * @notice Thrown when a withdrawal of ETH fails
     */
    error WithdrawFundsFailed();
    
    /**
     * @notice Thrown when attempting to withdraw zero tokens or ETH
     */
    error CannotWithdrawZeroAmount();
    
    /**
     * @notice Thrown when the native fee for bridging is higher than the provided value
     */
    error InsufficientFeeToCoverCost();
    
    /**
     * @notice Thrown when the minimum return amount is not satisfied
     */
    error InsufficientMinReturn();

    /**
     * @notice Thrown when liquid withdraw config is not set for the liquid token
     */
    error LiquidWithdrawConfigNotSet();

    /**
     * @notice Thrown when the boring queue has a different boring vault than expected
     */
    error InvalidBoringQueue();

    /**
     * @notice Thrown when refund wallet is not set and trying to transfer to refund wallet
     */
    error RefundWalletNotSet();

    /**
     * @notice Thrown when the return amount is less than min return
     */
    error InsufficientReturnAmount();

    /**
     * @notice Constructor that disables initializers
     * @dev Cannot be called again after deployment (UUPS pattern)
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(BinSponsor _binSponsor, address _dataProvider) {
        _disableInitializers();
        binSponsor = _binSponsor;
        dataProvider = IEtherFiDataProvider(_dataProvider);
    }

    /**
     * @notice Initializes the contract with role registry and token destination data
     * @dev Can only be called once due to initializer modifier
     * @param _roleRegistry Address of the role registry contract
     * @param _tokens Array of token addresses to configure
     * @param _destDatas Array of destination data corresponding to each token
     * @custom:throws ArrayLengthMismatch If arrays have different lengths
     * @custom:throws InvalidValue If any address parameter is zero
     * @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
     */
    function initialize(address _roleRegistry, address[] calldata _tokens, DestinationData[] calldata _destDatas) external initializer {
        __UpgradeableProxy_init(_roleRegistry);
        _setDestinationData(_tokens, _destDatas);
    }

    /**
     * @dev Internal function to access the contract's storage
     * @return $ Storage pointer to the SettlementDispatcherStorage struct
     */
    function _getSettlementDispatcherStorage() internal pure returns (SettlementDispatcherStorage storage $) {
        assembly {
            $.slot := SettlementDispatcherStorageLocation
        }
    }

    /**
     * @notice Function to fetch the destination data for a token
     * @param token Address of the token
     * @return Destination data struct for the specified token
     */
    function destinationData(address token) public view returns (DestinationData memory) {
        return _getSettlementDispatcherStorage().destinationData[token];
    }

    /**
     * @notice Function to set the destination data for an array of tokens
     * @dev Only callable by the role registry owner
     * @param tokens Addresses of tokens to configure
     * @param destDatas Destination data structs for respective tokens
     * @custom:throws ArrayLengthMismatch If arrays have different lengths
     * @custom:throws InvalidValue If any address parameter is zero
     * @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
     */
    function setDestinationData(address[] calldata tokens, DestinationData[] calldata destDatas) external onlyRoleRegistryOwner {
        _setDestinationData(tokens, destDatas);
    }

    /**
     * @notice Function to set the liquid asset withdraw queue 
     * @dev Only callable by the role registry owner
     * @param asset Address of the liquid asset
     * @param boringQueue Address of the boring queue
     * @custom:throws InvalidValue If any address parameter is zero
     * @custom:throws InvalidBoringQueue If the queue does not belong to the liquid asset
     */
    function setLiquidAssetWithdrawQueue(address asset, address boringQueue) external onlyRoleRegistryOwner {
        if (asset == address(0) || boringQueue == address(0)) revert InvalidValue();
        if (asset != address(IBoringOnChainQueue(boringQueue).boringVault())) revert InvalidBoringQueue();

        _getSettlementDispatcherStorage().liquidWithdrawQueue[asset] = boringQueue;

        emit LiquidWithdrawQueueSet(asset, boringQueue);
    }

    /**
     * @notice Returns the liquid asset withdraw queue
     * @param asset Address of the liquid asset
     * @return Boring Queue for liquid asset
     */
    function getLiquidAssetWithdrawQueue(address asset) external view returns (address) {
        return _getSettlementDispatcherStorage().liquidWithdrawQueue[asset];
    } 

    /**
     * @notice Returns the refund wallet address set in the data provider contract
     * @return Refund wallet address
     */
    function getRefundWallet() public view returns (address) {
        return dataProvider.getRefundWallet();
    }

    /**
     * @notice Function to withdraw liquid tokens inside the Settlement Dispatcher
     * @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
     * @param liquidToken Address of the liquid token
     * @param assetOut Address of the underlying token to receive
     * @param amount Amount of liquid tokens to withdraw
     * @param minReturn Acceptable min return amount of asset out
     * @param discount Acceptable discount in bps
     * @param secondsToDeadline Expiry deadline in seconds from now
     */
    function withdrawLiquidAsset(address liquidToken, address assetOut, uint128 amount, uint128 minReturn, uint16 discount, uint24 secondsToDeadline) external onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
        IBoringOnChainQueue boringQueue = IBoringOnChainQueue(_getSettlementDispatcherStorage().liquidWithdrawQueue[liquidToken]);
        if (address(boringQueue) == address(0)) revert LiquidWithdrawConfigNotSet();

        uint128 amountOutFromQueue = boringQueue.previewAssetsOut(assetOut, amount, discount);
        if (amountOutFromQueue < minReturn) revert InsufficientReturnAmount();

        IERC20(liquidToken).forceApprove(address(boringQueue), amount);
        boringQueue.requestOnChainWithdraw(assetOut, amount, discount, secondsToDeadline);

        emit LiquidWithdrawalRequested(liquidToken, assetOut, amount, amountOutFromQueue);
    }

    /**
     * @notice Transfers funds to the refund wallet
     * @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
     * @param asset Address of the token to transfer 
     * @param amount Amount of tokens to transfer
     * @custom:throws RefundWalletNotSet If the refund wallet address is not set
     * @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
     * @custom:throws WithdrawFundsFailed If ETH transfer fails
     */
    function transferFundsToRefundWallet(address asset, uint256 amount) external nonReentrant onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
        address refundWallet = getRefundWallet();
        if (refundWallet == address(0)) revert RefundWalletNotSet();
        amount = _withdrawFunds(asset, refundWallet, amount);

        emit TransferToRefundWallet(asset, refundWallet, amount);
    }

    /**
     * @notice Function to bridge tokens to another chain
     * @dev Only callable by addresses with SETTLEMENT_DISPATCHER_BRIDGER_ROLE
     * @param token Address of the token to bridge
     * @param amount Amount of the token to bridge
     * @param minReturnLD Minimum amount to receive on the destination chain
     * @custom:throws InvalidValue If token is address(0) or amount is 0
     * @custom:throws InsufficientBalance If the contract doesn't have enough tokens
     * @custom:throws DestinationDataNotSet If destination data is not set for the token
     * @custom:throws InsufficientMinReturn If the expected return is less than minReturnLD
     * @custom:throws InsufficientFeeToCoverCost If not enough ETH is provided for fees
     */
    function bridge(address token, uint256 amount, uint256 minReturnLD) external payable whenNotPaused onlyRole(SETTLEMENT_DISPATCHER_BRIDGER_ROLE) {
        if (token == address(0) || amount == 0) revert InvalidValue();
        
        uint256 balance = 0;
        if (token == ETH) balance = address(this).balance;
        else balance = IERC20(token).balanceOf(address(this));
        
        if (balance < amount) revert InsufficientBalance();

        DestinationData memory destData = _getSettlementDispatcherStorage().destinationData[token];
        if (destData.useCanonicalBridge) {
            _withdrawCanonicalBridge(token, destData.destRecipient, amount, destData.minGasLimit);
        }
        else {
            (address stargate, uint256 valueToSend, uint256 minReturnFromStargate, SendParam memory sendParam, MessagingFee memory messagingFee) = 
                prepareRideBus(token, amount);

            if (minReturnLD > minReturnFromStargate) revert InsufficientMinReturn();
            if (address(this).balance < valueToSend) revert InsufficientFeeToCoverCost();

            if (token != ETH) IERC20(token).forceApprove(stargate, amount);
            (, , Ticket memory ticket) = IStargate(stargate).sendToken{ value: valueToSend }(sendParam, messagingFee, payable(address(this)));
            emit FundsBridgedWithStargate(token, amount, ticket);
        }
    }

    /**
     * @notice Prepares parameters for the Stargate bridge transaction
     * @dev Uses Stargate's "Ride the Bus" pattern for token bridging
     * @param token Address of the token to bridge
     * @param amount Amount of the token to bridge
     * @return stargate Address of the Stargate router to use
     * @return valueToSend Amount of ETH needed for the transaction
     * @return minReturnFromStargate Minimum amount expected to be received on destination
     * @return sendParam Stargate SendParam struct with bridging details
     * @return messagingFee Stargate MessagingFee struct with fee details
     * @custom:throws InvalidValue If token is address(0) or amount is 0
     * @custom:throws InsufficientBalance If the contract doesn't have enough tokens
     * @custom:throws DestinationDataNotSet If destination data is not set for the token
     */
    function prepareRideBus(
        address token,
        uint256 amount
    ) public view returns (address stargate, uint256 valueToSend, uint256 minReturnFromStargate, SendParam memory sendParam, MessagingFee memory messagingFee) {
        
        DestinationData memory destData = _getSettlementDispatcherStorage().destinationData[token];
        if (destData.destRecipient == address(0)) revert DestinationDataNotSet();

        stargate = destData.stargate;
        sendParam = SendParam({
            dstEid: destData.destEid,
            to: bytes32(uint256(uint160(destData.destRecipient))),
            amountLD: amount,
            minAmountLD: amount,
            extraOptions: new bytes(0),
            composeMsg: new bytes(0),
            oftCmd: new bytes(1)
        });

        (, , OFTReceipt memory receipt) = IStargate(stargate).quoteOFT(sendParam);
        sendParam.minAmountLD = receipt.amountReceivedLD;
        minReturnFromStargate = receipt.amountReceivedLD;

        messagingFee = IStargate(stargate).quoteSend(sendParam, false);
        valueToSend = messagingFee.nativeFee;

        if (IStargate(stargate).token() == address(0x0)) {
            valueToSend += sendParam.amountLD;
        }
    }

    /**
     * @notice Withdraws tokens or ETH from the contract
     * @dev Only callable by the role registry owner
     * @param token Address of the token to withdraw (address(0) for ETH)
     * @param recipient Address to receive the withdrawn funds
     * @param amount Amount to withdraw (0 to withdraw all)
     * @custom:throws InvalidValue If recipient is address(0)
     * @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
     * @custom:throws WithdrawFundsFailed If ETH transfer fails
     */
    function withdrawFunds(address token, address recipient, uint256 amount) external nonReentrant onlyRoleRegistryOwner() {
        if (recipient == address(0)) revert InvalidValue();
        amount = _withdrawFunds(token, recipient, amount);
        emit FundsWithdrawn(token, amount, recipient);
    }

    /**
     * @notice Withdraw tokens from scroll with the canonical bridge
     * @dev Used by bridge function
     * @param token Address of the token to withdraw
     * @param recipient Address to receive the funds on ethereum
     * @param amount Amount to withdraw
     * @param minGasLimit Minimum gas limit for the withdrawal
     */
    function _withdrawCanonicalBridge(address token, address recipient, uint256 amount, uint256 minGasLimit) internal returns (uint256) {
        if (token == ETH) {
            IL2Messenger(ETH_MESSENGER).sendMessage{value: amount}(recipient, amount, "", minGasLimit);
        }
        else {
            address gateway = IL2GatewayRouter(GATEWAY_ROUTER).getERC20Gateway(token);

            IERC20(token).forceApprove(gateway, amount);
            IL2GatewayRouter(GATEWAY_ROUTER).withdrawERC20(token, recipient, amount, minGasLimit);
        }

        emit CanonicalBridgeWithdraw(token, recipient, amount);
        return amount;
    }
    
    /**
     * @notice Internal function to handle withdrawal of tokens or ETH
     * @dev Used by both withdrawFunds and transferFundsToRefundWallet
     * @param token Address of the token to withdraw 
     * @param recipient Address to receive the withdrawn funds
     * @param amount Amount to withdraw (0 to withdraw all available balance)
     * @custom:throws CannotWithdrawZeroAmount If attempting to withdraw zero tokens or ETH
     * @custom:throws WithdrawFundsFailed If ETH transfer fails
     */    
    function _withdrawFunds(address token, address recipient, uint256 amount) internal returns (uint256) {
        if (token == ETH) {
            if (amount == 0) amount = address(this).balance;
            if (amount == 0) revert CannotWithdrawZeroAmount();
            (bool success, ) = payable(recipient).call{value: amount}("");
            if (!success) revert  WithdrawFundsFailed();
        } else {
            if (amount == 0) amount = IERC20(token).balanceOf(address(this));
            if (amount == 0) revert CannotWithdrawZeroAmount();
            IERC20(token).safeTransfer(recipient, amount);
        }

        return amount;
    }

    /**
     * @notice Internal function to set destination data for tokens
     * @dev Validates and stores destination data for each token
     * @param tokens Array of token addresses to configure
     * @param destDatas Array of destination data corresponding to each token
     * @custom:throws ArrayLengthMismatch If arrays have different lengths
     * @custom:throws InvalidValue If any address parameter is zero
     * @custom:throws StargateValueInvalid If the Stargate router doesn't support the token
     */
    function _setDestinationData(address[] calldata tokens, DestinationData[] calldata destDatas) internal {
        uint256 len = tokens.length;
        if (len != destDatas.length) revert ArrayLengthMismatch(); 

        SettlementDispatcherStorage storage $ = _getSettlementDispatcherStorage();

        for (uint256 i = 0; i < len; ) {
            if (destDatas[i].useCanonicalBridge) {
                if (tokens[i] == address(0) || destDatas[i].destRecipient == address(0) || destDatas[i].stargate != address(0) ||  destDatas[i].destEid != 0) revert InvalidValue();
            }
            else {
                if (tokens[i] == address(0) || destDatas[i].destRecipient == address(0) || destDatas[i].stargate == address(0)) revert InvalidValue(); 
            
                if (tokens[i] == ETH) {
                    if (IStargate(destDatas[i].stargate).token() != address(0)) revert StargateValueInvalid(); 
                }
                else if (IStargate(destDatas[i].stargate).token() != tokens[i]) revert StargateValueInvalid();
            }

            $.destinationData[tokens[i]] = destDatas[i];
            unchecked {
                ++i;
            }
        }

        emit DestinationDataSet(tokens, destDatas);
    }

    /**
     * @notice Fallback function to receive ETH
     * @dev Required to receive fee refunds from Stargate
     */
    receive() external payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.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 {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @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 {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @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 {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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 high-level call (i.e. a regular function call to a contract), 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 data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), 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 data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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.28;

// Solidity does not support splitting import across multiple lines
// solhint-disable-next-line max-line-length
import { IOFT, MessagingFee, MessagingReceipt, OFTReceipt, SendParam } from "./IOFT.sol";

/// @notice Stargate implementation type.
enum StargateType {
    Pool,
    OFT
}

/// @notice Ticket data for bus ride.
struct Ticket {
    uint72 ticketId;
    bytes passengerBytes;
}

/// @title Interface for Stargate.
/// @notice Defines an API for sending tokens to destination chains.
interface IStargate is IOFT {
    /// @dev This function is same as `send` in OFT interface but returns the ticket data if in the bus ride mode,
    /// which allows the caller to ride and drive the bus in the same transaction.
    function sendToken(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt, Ticket memory ticket);

    /// @notice Returns the Stargate implementation type.
    function stargateType() external pure returns (StargateType);

    function token() external view returns (address);
}

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

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

/**
 * @title IEtherFiDataProvider
 * @author ether.fi
 * @notice Interface for the EtherFiDataProvider contract that manages important data for ether.fi
 */
interface IEtherFiDataProvider {
    /**
     * @notice Configures multiple modules' whitelist status
     * @dev Only callable by addresses with ADMIN_ROLE
     * @param modules Array of module addresses to configure
     * @param shouldWhitelist Array of boolean values indicating whether each module should be whitelisted
     */
    function configureModules(address[] calldata modules, bool[] calldata shouldWhitelist) external;

    /**
     * @notice Updates the hook address
     * @dev Only callable by addresses with ADMIN_ROLE
     * @param hook New hook address to set
     */
    function setHookAddress(address hook) external;

    /**
     * @notice Updates the address of the Cash Module
     * @dev Only callable by addresses with ADMIN_ROLE
     * @param cashModule New cash module address to set
     */
    function setCashModule(address cashModule) external;

    /**
     * @notice Checks if a module address is whitelisted
     * @param module Address to check
     * @return bool True if the module is whitelisted, false otherwise
     */
    function isWhitelistedModule(address module) external view returns (bool);

    /**
     * @notice Checks if a module address is a whitelisted default module
     * @param module Address to check
     * @return bool True if the module is a whitelisted default module, false otherwise
     */
    function isDefaultModule(address module) external view returns (bool);

    /**
     * @notice Retrieves all whitelisted module addresses
     * @return address[] Array of whitelisted module addresses
     */
    function getWhitelistedModules() external view returns (address[] memory);

    /**
     * @notice Returns the address of the Cash Module
     * @return Address of the cash module
     */
    function getCashModule() external view returns (address);

    /**
     * @notice Returns the address of the EtherFi Recovery signer
     * @return Address of the EtherFi Recovery Signer
     */
    function getEtherFiRecoverySigner() external view returns (address);

     /**
     * @notice Returns the address of the Third Party Recovery signer
     * @return Address of the Third Party Recovery Signer
     */
    function getThirdPartyRecoverySigner() external view returns (address);

        /**
     * @notice Returns the address of the Refund wallet
     * @return Address of the Refund wallet
     */
    function getRefundWallet() external view returns (address);

    /**
     * @notice Returns the Recovery delay period in seconds
     * @return Recovery delay period in seconds
     */
    function getRecoveryDelayPeriod() external view returns (uint256);

    /**
     * @notice Returns the address of the Cash Lens contract
     * @return Address of the Cash Lens contract
     */
    function getCashLens() external view returns (address);

    /**
     * @notice Returns the address of the Price provider contract
     * @return Address of the Price provider contract
     */
    function getPriceProvider() external view returns (address);

    /**
     * @notice Returns the current hook address
     * @return address Current hook address
     */
    function getHookAddress() external view returns (address);

    function getEtherFiSafeFactory() external view returns (address);

    /**
     * @notice Function to check if an account is an EtherFiSafe
     * @param account Address of the account to check
     */
    function isEtherFiSafe(address account) external view returns (bool);

    /**
     * @notice Role identifier for administrative privileges
     * @return bytes32 The keccak256 hash of "ADMIN_ROLE"
     */
    function ADMIN_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the address of the Role Registry contract
     * @return roleRegistry Reference to the role registry contract
     */
    function roleRegistry() external view returns (IRoleRegistry);
}

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

interface IBoringOnChainQueue {
    /**
     * @param allowWithdraws Whether or not withdraws are allowed for this asset.
     * @param secondsToMaturity The time in seconds it takes for the asset to mature.
     * @param minimumSecondsToDeadline The minimum time in seconds a withdraw request must be valid for before it is expired
     * @param minDiscount The minimum discount allowed for a withdraw request.
     * @param maxDiscount The maximum discount allowed for a withdraw request.
     * @param minimumShares The minimum amount of shares that can be withdrawn.
     */
    struct WithdrawAsset {
        bool allowWithdraws;
        uint24 secondsToMaturity;
        uint24 minimumSecondsToDeadline;
        uint16 minDiscount;
        uint16 maxDiscount;
        uint96 minimumShares;
    }

    /**
     * @notice Gets the withdrawal config for the asset out
     * @param assetOut Address of asset out
     * @return WithdrawAsset struct
     */
    function withdrawAssets(address assetOut) external view returns (WithdrawAsset memory);

    /**
     * @notice Returns the boring vault address
     */
    function boringVault() external view returns (BoringVault);

    /**
     * @notice Request an on-chain withdraw.
     * @param assetOut The asset to withdraw.
     * @param amountOfShares The amount of shares to withdraw.
     * @param discount The discount to apply to the withdraw in bps.
     * @param secondsToDeadline The time in seconds the request is valid for.
     * @return requestId The request Id.
     */
    function requestOnChainWithdraw(address assetOut, uint128 amountOfShares, uint16 discount, uint24 secondsToDeadline) external returns (bytes32 requestId);

    /**
     * @notice Preview assets out from a withdraw request.
     * @param assetOut The asset to withdraw.
     * @param amountOfShares The amount of shares to withdraw.
     * @param discount The discount to apply to the withdraw in bps.
     * @return amountOfAssets128 Amount of assets out.
     */
    function previewAssetsOut(address assetOut, uint128 amountOfShares, uint16 discount) external view returns (uint128 amountOfAssets128);
}

contract BoringVault {}

File 7 of 38 : ICashModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { EnumerableSetLib } from "solady/utils/EnumerableSetLib.sol";

import { IDebtManager } from "../interfaces/IDebtManager.sol";
import { IEtherFiDataProvider } from "../interfaces/IEtherFiDataProvider.sol";
import { IPriceProvider } from "../interfaces/IPriceProvider.sol";
import { SpendingLimit, SpendingLimitLib } from "../libraries/SpendingLimitLib.sol";

/**
 * @title CashbackTypes
 * @notice Defines the different cashback types
 */
enum CashbackTypes {
    Regular,
    Spender, 
    Promotion,
    Referral
}

/**
 * @title Cashback
 * @notice Defines the structure of Cashback
 */
struct Cashback {
    address to;
    CashbackTokens[] cashbackTokens;
}

/**
 * @title CashbackTokens
 * @notice Defines the structure of Cashback tokens 
 */
struct CashbackTokens {
    address token;
    uint256 amountInUsd;
    CashbackTypes cashbackType;
}

/**
 * @title TokenDataInUsd
 * @notice Defines the token data with amount in USD
 */
struct TokenDataInUsd {
    address token;
    uint256 amountInUsd;
}

/**
 * @title BinSponsor
 * @notice Defines the bin sponsors or card issuers
 */
enum BinSponsor {
    Reap,
    Rain
}

/**
 * @title Mode
 * @notice Defines the operating mode for cash spending operations
 * @dev Credit mode borrows tokens, Debit mode uses balance from the safe
 */
enum Mode {
    Credit,
    Debit
}

/**
 * @title WithdrawalRequest
 * @notice Structure representing a pending withdrawal of tokens
 * @dev Includes tokens, amounts, recipient, and finalization timestamp
 */
struct WithdrawalRequest {
    address[] tokens;
    uint256[] amounts;
    address recipient;
    uint96 finalizeTime;
}

/**
 * @title SafeData
 * @notice View-only representation of a Safe's cash configuration
 * @dev Used for returning Safe data in external-facing functions
 */
struct SafeData {
    /// @notice Spend limit for the user
    SpendingLimit spendingLimit;
    /// @notice Impending withdrawal request for the user
    WithdrawalRequest pendingWithdrawalRequest;
    /// @notice User's chosen mode
    Mode mode;
    /// @notice Start time for credit mode
    uint256 incomingCreditModeStartTime;
    /// @notice Running total of all cashback earned by this safe (and its spenders) in USD
    uint256 totalCashbackEarnedInUsd;
}

/**
 * @notice SafeTiers
 * @dev Gets cashback based on the safe tier
 */
enum SafeTiers {
    Pepe,
    Wojak,
    Chad,
    Whale,
    Business
}

/**
 * @title SafeCashConfig
 * @notice Complete storage representation of a Safe's cash configuration
 * @dev Includes all data needed for managing a Safe's cash operations
 */
struct SafeCashConfig {
    /// @notice Spending limit configuration including daily and monthly limits
    SpendingLimit spendingLimit;
    /// @notice Pending withdrawal request with token addresses, amounts, recipient, and finalization time
    WithdrawalRequest pendingWithdrawalRequest;
    /// @notice Current operating mode (Credit or Debit) for spending transactions
    Mode mode;
    /// @notice Timestamp when a pending change to Credit mode will take effect (0 if no pending change)
    uint256 incomingCreditModeStartTime;
    /// @notice Tier level of the safe that determines cashback percentages
    SafeTiers safeTier;
    /// @notice Mapping of transaction IDs to cleared status to prevent replay attacks
    mapping(bytes32 txId => bool cleared) transactionCleared;
    /// @notice Percentage of cashback allocated to the safe vs. the spender (in basis points, 5000 = 50%)
    uint256 DEPRECATED_cashbackSplitToSafePercentage;
    /// @notice Running total of all cashback earned by this safe (and its spenders) in USD
    uint256 totalCashbackEarnedInUsd;
}

/**
 * @title SafeCashData
 * @notice Comprehensive data structure for front-end display of Safe cash state
 * @dev Aggregates data from multiple sources for a complete view of a Safe's financial state
 */
struct SafeCashData {
    /// @notice Current operating mode (Credit or Debit)
    Mode mode;
    /// @notice Array of collateral token balances
    IDebtManager.TokenData[] collateralBalances;
    /// @notice Array of borrowed token balances
    IDebtManager.TokenData[] borrows;
    /// @notice Array of token prices
    IDebtManager.TokenData[] tokenPrices;
    /// @notice Current withdrawal request
    WithdrawalRequest withdrawalRequest;
    /// @notice Total value of collateral in USD
    uint256 totalCollateral;
    /// @notice Total value of borrows in USD
    uint256 totalBorrow;
    /// @notice Maximum borrowing power in USD
    uint256 maxBorrow;
    /// @notice Maximum spendable amount in Credit mode (USD)
    uint256 creditMaxSpend;
    /// @notice Remaining spending limit allowance
    uint256 spendingLimitAllowance;
    /// @notice Running total of all cashback earned by this safe (and its spenders) in USD
    uint256 totalCashbackEarnedInUsd;
    /// @notice Timestamp when a pending change to Credit mode will take effect (0 if no pending change)
    uint256 incomingCreditModeStartTime;
    /// @notice Maximum spendable amount in Debit mode 
    DebitModeMaxSpend debitMaxSpend;
}

/**
 * @title DebitModeMaxSpend
 * @notice Data structure to return Debit mode max spend result
 */
struct DebitModeMaxSpend {
    /// @notice Tokens that can be spent. Order of token matter, Order determines 
    /// priority for deficit coverage - earlier tokens are used first
    address[] spendableTokens;
    /// @notice Amounts of respective tokens that can be spent
    uint256[] spendableAmounts;
    /// @notice Amounts in USD of respective tokens that can be spent
    uint256[] amountsInUsd;
    /// @notice Total amount in USD that can be spent
    uint256 totalSpendableInUsd;
}

interface ICashModule {
    /// @notice Error thrown when a transaction has already been cleared
    error TransactionAlreadyCleared();

    /// @notice Error thrown when a non-EtherFi wallet tries to access restricted functions
    error OnlyEtherFiWallet();

    /// @notice Error thrown when an unsupported token is used
    error UnsupportedToken();

    /// @notice Error thrown when a token that is not a borrow token is used in certain operations
    error OnlyBorrowToken();

    /// @notice Error thrown when an operation amount is zero
    error AmountZero();

    /// @notice Error thrown when a balance is insufficient for an operation
    error InsufficientBalance();

    /// @notice Error thrown when borrowings would exceed maximum allowed after a spending operation
    error BorrowingsExceedMaxBorrowAfterSpending();

    /// @notice Error thrown when a recipient address is zero
    error RecipientCannotBeAddressZero();

    /// @notice Error thrown when caller doesn't have the Cash Module Controller role
    error OnlyCashModuleController();

    /// @notice Error thrown when a withdrawal is attempted before the delay period
    error CannotWithdrawYet();

    /// @notice Error thrown when signature verification fails
    error InvalidSignatures();

    /// @notice Error thrown when trying to set a mode that's already active
    error ModeAlreadySet();

    /// @notice Error thrown when a non-DebtManager contract calls restricted functions
    error OnlyDebtManager();

    /// @notice Error thrown when trying to set a tier that's already set
    error AlreadyInSameTier(uint256 index);

    /// @notice Error thrown when a cashback percentage exceeds max allowed
    error CashbackPercentageGreaterThanMaxAllowed();

    /// @notice Error thrown when trying to set a split percentage that's already set
    error SplitAlreadyTheSame();

    /// @notice Throws when the msg.sender is not an admin to the safe
    error OnlySafeAdmin();

    /// @notice Thrown when the input is invalid
    error InvalidInput();

    /// @notice Thrown when the signature verification fails
    error InvalidSignature();

    /// @notice Thrown when there is an array length mismatch
    error ArrayLengthMismatch();

    /// @notice Thrown when the caller is not an EtherFi Safe
    error OnlyEtherFiSafe();

    /// @notice Error thrown when trying to use multiple tokens in credit mode
    error OnlyOneTokenAllowedInCreditMode();

    /// @notice Error thrown when trying to withdraw an non whitelisted asset
    /// @param asset The address of the invalid asset
    error InvalidWithdrawAsset(address asset);

    /**
     * @notice Role identifier for EtherFi wallet access control
     * @return The role identifier as bytes32
     */
    function ETHER_FI_WALLET_ROLE() external pure returns (bytes32);

    /**
     * @notice Role identifier for Cash Module controller access
     * @return The role identifier as bytes32
     */
    function CASH_MODULE_CONTROLLER_ROLE() external pure returns (bytes32);

    /**
     * @notice Maximum allowed cashback percentage (10%)
     * @return The maximum percentage in basis points
     */
    function MAX_CASHBACK_PERCENTAGE() external pure returns (uint256);

    /**
     * @notice Represents 100% in basis points (10,000)
     * @return 100% value in basis points
     */
    function HUNDRED_PERCENT_IN_BPS() external pure returns (uint256);

    /**
     * @notice Returns if an asset is a whitelisted withdraw asset
     * @param asset Address of the asset
     * @return True if asset is whitelisted for withdrawals, false otherwise
     */
    function isWhitelistedWithdrawAsset(address asset) external view returns (bool);

    /**
     * @notice Returns all the assets whitelisted for withdrawals
     * @return Array of whitelisted withdraw assets
     */    
    function getWhitelistedWithdrawAssets() external view returns (address[] memory);

    /**
     * @notice Retrieves cash configuration data for a Safe
     * @param safe Address of the EtherFi Safe
     * @return Data structure containing Safe cash configuration
     * @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
     */
    function getData(address safe) external view returns (SafeData memory);

    /**
     * @notice Checks if a transaction has been cleared
     * @param safe Address of the EtherFi Safe
     * @param txId Transaction identifier
     * @return Boolean indicating if the transaction is cleared
     * @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
     */
    function transactionCleared(address safe, bytes32 txId) external view returns (bool);

    /**
     * @notice Gets the debt manager contract
     * @return IDebtManager instance
     */
    function getDebtManager() external view returns (IDebtManager);

    /**
     * @notice Gets the settlement dispatcher address
     * @dev The settlement dispatcher receives the funds that are spent
     * @param binSponsor Bin sponsor for which the settlement dispatcher needs to be returned
     * @return settlementDispatcher The address of the settlement dispatcher
     */
    function getSettlementDispatcher(BinSponsor binSponsor) external view returns (address settlementDispatcher);

    /**
     * @notice Returns the EtherFiDataProvider contract reference
     * @dev Used to access global system configuration and services
     * @return The EtherFiDataProvider contract instance
     */
    function etherFiDataProvider() external view returns (IEtherFiDataProvider);

    /**
     * @notice Gets the pending cashback amount for an account in USD
     * @dev Returns the amount of cashback waiting to be claimed
     * @param account Address of the account (safe or spender)
     * @param tokens Addresses of tokens for cashback
     * @return data Pending cashback data for tokens in USD
     * @return totalCashbackInUsd Total pending cashback amount in USD
     */
    function getPendingCashback(address account, address[] memory tokens) external view returns (TokenDataInUsd[] memory data, uint256 totalCashbackInUsd);

    /**
     * @notice Gets the pending cashback amount for an account in USD for a specific token
     * @dev Returns the amount of cashback waiting to be claimed
     * @param account Address of the account (safe or spender)
     * @param token Address of tokens for cashback
     * @return Pending cashback amount in USD for the token
     */
    function getPendingCashbackForToken(address account, address token) external view returns (uint256);

    /**
     * @notice Gets the current delay settings for the module
     * @return withdrawalDelay Delay in seconds before a withdrawal can be finalized
     * @return spendLimitDelay Delay in seconds before spending limit changes take effect
     * @return modeDelay Delay in seconds before a mode change takes effect
     */
    function getDelays() external view returns (uint64, uint64, uint64);

    /**
     * @notice Gets the current operating mode of a safe
     * @dev Considers pending mode changes that have passed their delay
     * @param safe Address of the EtherFi Safe
     * @return The current operating mode (Debit or Credit)
     */
    function getMode(address safe) external view returns (Mode);

    /**
     * @notice Gets the timestamp when a pending credit mode change will take effect
     * @dev Returns 0 if no pending change or if the safe uses debit mode
     * @param safe Address of the EtherFi Safe
     * @return Timestamp when credit mode will take effect, or 0 if not applicable
     */
    function incomingCreditModeStartTime(address safe) external view returns (uint256);

    /**
     * @notice Gets the pending withdrawal amount for a token
     * @dev Only callable for valid EtherFi Safe addresses
     * @param safe Address of the EtherFi Safe
     * @param token Address of the token to check
     * @return Amount of tokens pending withdrawal
     * @custom:throws OnlyEtherFiSafe if the address is not a valid EtherFi Safe
     */
    function getPendingWithdrawalAmount(address safe, address token) external view returns (uint256);

    /**
     * @notice Sets up a new Safe's Cash Module with initial configuration
     * @dev Creates default spending limits and sets initial mode to Debit with 50% cashback split
     * @param data The encoded initialization data containing daily limit, monthly limit, and timezone offset
     * @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
     */
    function setupModule(bytes calldata data) external;

    /**
     * @notice Initializes the CashModule contract
     * @dev Sets up the role registry, debt manager, settlement dispatcher, and data providers
     * @param _roleRegistry Address of the role registry contract
     * @param _debtManager Address of the debt manager contract
     * @param _settlementDispatcherReap Address of the settlement dispatcher for Reap
     * @param _settlementDispatcherRain Address of the settlement dispatcher for Rain
     * @param _cashbackDispatcher Address of the cashback dispatcher
     * @param _cashEventEmitter Address of the cash event emitter
     * @param _cashModuleSetters Address of the cash module setters contract
     * @custom:throws InvalidInput if any essential address is zero
     */
    function initialize(address _roleRegistry, address _debtManager, address _settlementDispatcherReap, address _settlementDispatcherRain, address _cashbackDispatcher, address _cashEventEmitter, address _cashModuleSetters) external;

    /**
     * @notice Configures the withdraw assets whitelist
     * @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
     * @param assets Array of asset addresses to configure 
     * @param shouldWhitelist Array of boolean suggesting whether to whitelist the assets
     * @custom:throws OnlyCashModuleController if the caller does not have CASH_MODULE_CONTROLLER_ROLE role
     * @custom:throws InvalidInput If the arrays are empty
     * @custom:throws ArrayLengthMismatch If the arrays have different lengths
     * @custom:throws InvalidAddress If any address is the zero address
     * @custom:throws DuplicateElementFound If any address appears more than once in the addrs array
     */
    function configureWithdrawAssets(address[] calldata assets, bool[] calldata shouldWhitelist) external;

    /**
     * @notice Sets the settlement dispatcher address for a bin sponsor
     * @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
     * @param binSponsor Bin sponsor for which the settlement dispatcher is updated
     * @param dispatcher Address of the new settlement dispatcher for the bin sponsor
     * @custom:throws InvalidInput if caller doesn't have the controller role
     */
    function setSettlementDispatcher(BinSponsor binSponsor, address dispatcher) external;

    /**
     * @notice Sets the tier for one or more safes
     * @dev Assigns tiers which determine cashback percentages
     * @param safes Array of safe addresses to update
     * @param tiers Array of tiers to assign to the corresponding safe addresses
     * @custom:throws OnlyEtherFiWallet if caller doesn't have the wallet role
     * @custom:throws ArrayLengthMismatch if arrays have different lengths
     * @custom:throws OnlyEtherFiSafe if any address is not a valid EtherFi Safe
     * @custom:throws AlreadyInSameTier if a safe is already in the specified tier
     */
    function setSafeTier(address[] memory safes, SafeTiers[] memory tiers) external;

    /**
     * @notice Sets the time delays for withdrawals, spending limit changes, and mode changes
     * @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
     * @param withdrawalDelay Delay in seconds before a withdrawal can be finalized
     * @param spendLimitDelay Delay in seconds before spending limit changes take effect
     * @param modeDelay Delay in seconds before a mode change takes effect
     * @custom:throws OnlyCashModuleController if caller doesn't have the controller role
     */
    function setDelays(uint64 withdrawalDelay, uint64 spendLimitDelay, uint64 modeDelay) external;

    /**
     * @notice Sets the operating mode for a safe
     * @dev Switches between Debit and Credit modes, with possible delay for Credit mode
     * @param safe Address of the EtherFi Safe
     * @param mode The target mode (Debit or Credit)
     * @param signer Address of the safe admin signing the transaction
     * @param signature Signature from the signer authorizing this mode change
     * @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
     * @custom:throws OnlySafeAdmin if signer is not a safe admin
     * @custom:throws ModeAlreadySet if the safe is already in the requested mode
     * @custom:throws InvalidSignatures if signature verification fails
     */
    function setMode(address safe, Mode mode, address signer, bytes calldata signature) external;

    /**
     * @notice Updates the spending limits for a safe
     * @dev Can only be called by the safe itself with a valid admin signature
     * @param safe Address of the EtherFi Safe
     * @param dailyLimitInUsd New daily spending limit in USD
     * @param monthlyLimitInUsd New monthly spending limit in USD
     * @param signer Address of the safe admin signing the transaction
     * @param signature Signature from the signer authorizing this update
     * @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
     * @custom:throws OnlySafeAdmin if signer is not a safe admin
     * @custom:throws InvalidSignatures if signature verification fails
     */
    function updateSpendingLimit(address safe, uint256 dailyLimitInUsd, uint256 monthlyLimitInUsd, address signer, bytes calldata signature) external;

    /**
     * @notice Processes a spending transaction with multiple tokens
     * @dev Only callable by EtherFi wallet for valid EtherFi Safe addresses
     * @param safe Address of the EtherFi Safe
     * @param txId Transaction identifier
     * @param binSponsor Bin sponsor used for spending
     * @param tokens Array of addresses of the tokens to spend
     * @param amountsInUsd Array of amounts to spend in USD (must match tokens array length)
     * @param cashbacks Struct of Cashback to be given
     * @custom:throws TransactionAlreadyCleared if the transaction was already processed
     * @custom:throws UnsupportedToken if any token is not supported
     * @custom:throws AmountZero if any converted amount is zero
     * @custom:throws ArrayLengthMismatch if token and amount arrays have different lengths
     * @custom:throws OnlyOneTokenAllowedInCreditMode if multiple tokens are used in credit mode
     * @custom:throws If spending would exceed limits or balances
     */
    function spend(address safe, bytes32 txId, BinSponsor binSponsor,  address[] calldata tokens,  uint256[] calldata amountsInUsd,  Cashback[] calldata cashbacks) external;

    /**
     * @notice Clears pending cashback for users
     * @param users Addresses of users to clear the pending cashback for
     * @param tokens Addresses of cashback tokens
     */
    function clearPendingCashback(address[] calldata users, address[] calldata tokens) external;

    /**
     * @notice Repays borrowed tokens
     * @dev Only callable by EtherFi wallet for valid EtherFi Safe addresses
     * @param safe Address of the EtherFi Safe
     * @param token Address of the token to repay
     * @param amountInUsd Amount to repay in USD
     * @custom:throws OnlyEtherFiWallet if caller doesn't have the wallet role
     * @custom:throws OnlyEtherFiSafe if the safe is not a valid EtherFi Safe
     * @custom:throws OnlyBorrowToken if token is not a valid borrow token
     * @custom:throws AmountZero if the converted amount is zero
     * @custom:throws InsufficientBalance if there is not enough balance for the operation
     */
    function repay(address safe, address token, uint256 amountInUsd) external;

    /**
     * @notice Requests a withdrawal of tokens to a recipient
     * @dev Creates a pending withdrawal request that can be processed after the delay period
     * @dev Can only be done by the quorum of owners of the safe
     * @param safe Address of the EtherFi Safe
     * @param tokens Array of token addresses to withdraw
     * @param amounts Array of token amounts to withdraw
     * @param recipient Address to receive the withdrawn tokens 
     * @param signers Array of safe owner addresses signing the transaction
     * @param signatures Array of signatures from the safe owners
     * @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
     * @custom:throws InvalidSignatures if signature verification fails
     * @custom:throws RecipientCannotBeAddressZero if recipient is the zero address
     * @custom:throws ArrayLengthMismatch if arrays have different lengths
     * @custom:throws InsufficientBalance if any token has insufficient balance
     */
    function requestWithdrawal(address safe, address[] calldata tokens, uint256[] calldata amounts, address recipient, address[] calldata signers, bytes[] calldata signatures) external;

    /**
     * @notice Processes a pending withdrawal request after the delay period
     * @dev Executes the token transfers and clears the request
     * @param safe Address of the EtherFi Safe
     * @custom:throws OnlyEtherFiSafe if the caller is not a valid EtherFi Safe
     * @custom:throws CannotWithdrawYet if the withdrawal delay period hasn't passed
     */
    function processWithdrawal(address safe) external;

    /**
     * @notice Prepares a safe for liquidation by canceling any pending withdrawals
     * @dev Only callable by the DebtManager
     * @param safe Address of the EtherFi Safe being liquidated
     * @custom:throws OnlyDebtManager if called by any address other than the DebtManager
     */
    function preLiquidate(address safe) external;

    /**
     * @notice Executes post-liquidation logic to transfer tokens to the liquidator
     * @dev Only callable by the DebtManager after a successful liquidation
     * @param safe Address of the EtherFi Safe being liquidated
     * @param liquidator Address that will receive the liquidated tokens
     * @param tokensToSend Array of token data with amounts to send to the liquidator
     * @custom:throws OnlyDebtManager if called by any address other than the DebtManager
     */
    function postLiquidate(address safe, address liquidator, IDebtManager.LiquidationTokenData[] memory tokensToSend) external;

    /**
     * @notice Returns the current nonce for a Safe
     * @param safe The Safe address to query
     * @return Current nonce value
     * @dev Nonces are used to prevent signature replay attacks
     */
    function getNonce(address safe) external view returns (uint256);

    /**
     * @notice Sets the new CashModuleSetters implementation address
     * @dev Only callable by accounts with CASH_MODULE_CONTROLLER_ROLE
     * @param newCashModuleSetters Address of the new CashModuleSetters implementation
     * @custom:throws OnlyCashModuleController if caller doesn't have the controller role
     * @custom:throws InvalidInput if newCashModuleSetters = address(0)
     */
    function setCashModuleSettersAddress(address newCashModuleSetters) external;

    /**
     * @notice Fetched the safe tier
     * @param safe Address of the safe
     * @return SafeTiers Tier of the safe
     */
    function getSafeTier(address safe) external view returns (SafeTiers);
}

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

struct SendParam {
    uint32 dstEid; // Destination endpoint ID.
    bytes32 to; // Recipient address.
    uint256 amountLD; // Amount to send in local decimals.
    uint256 minAmountLD; // Minimum amount to send in local decimals.
    bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
    bytes composeMsg; // The composed message for the send() operation.
    bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}

struct MessagingParams {
    uint32 dstEid;
    bytes32 receiver;
    bytes message;
    bytes options;
    bool payInLzToken;
}

struct MessagingReceipt {
    bytes32 guid;
    uint64 nonce;
    MessagingFee fee;
}

struct MessagingFee {
    uint256 nativeFee;
    uint256 lzTokenFee;
}

/**
 * @dev Struct representing OFT limit information.
 * @dev These amounts can change dynamically and are up the the specific oft implementation.
 */
struct OFTLimit {
    uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
    uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}

/**
 * @dev Struct representing OFT receipt information.
 */
struct OFTReceipt {
    uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
    // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
    uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}

/**
 * @dev Struct representing OFT fee details.
 * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
 */
struct OFTFeeDetail {
    int256 feeAmountLD; // Amount of the fee in local decimals.
    string description; // Description of the fee.
}

/**
 * @title IOFT
 * @dev Interface for the OftChain (OFT) token.
 * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
 * @dev This specific interface ID is '0x02e49c2c'.
 */
interface IOFT {
    // Custom error messages
    error InvalidLocalDecimals();
    error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);

    // Events
    event OFTSent( // GUID of the OFT message.
        // Destination Endpoint ID.
        // Address of the sender on the src chain.
        // Amount of tokens sent in local decimals.
        // Amount of tokens received in local decimals.
    bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD);
    event OFTReceived( // GUID of the OFT message.
        // Source Endpoint ID.
        // Address of the recipient on the dst chain.
        // Amount of tokens received in local decimals.
    bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD);

    /**
     * @notice Retrieves interfaceID and the version of the OFT.
     * @return interfaceId The interface ID.
     * @return version The version.
     *
     * @dev interfaceId: This specific interface ID is '0x02e49c2c'.
     * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
     * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
     * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
     */
    function oftVersion() external view returns (bytes4 interfaceId, uint64 version);

    /**
     * @notice Retrieves the address of the token associated with the OFT.
     * @return token The address of the ERC20 token implementation.
     */
    function token() external view returns (address);

    /**
     * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
     * @return requiresApproval Needs approval of the underlying token implementation.
     *
     * @dev Allows things like wallet implementers to determine integration requirements,
     * without understanding the underlying token implementation.
     */
    function approvalRequired() external view returns (bool);

    /**
     * @notice Retrieves the shared decimals of the OFT.
     * @return sharedDecimals The shared decimals of the OFT.
     */
    function sharedDecimals() external view returns (uint8);

    /**
     * @notice Provides a quote for OFT-related operations.
     * @param _sendParam The parameters for the send operation.
     * @return limit The OFT limit information.
     * @return oftFeeDetails The details of OFT fees.
     * @return receipt The OFT receipt information.
     */
    function quoteOFT(SendParam calldata _sendParam) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);

    /**
     * @notice Provides a quote for the send() operation.
     * @param _sendParam The parameters for the send() operation.
     * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return fee The calculated LayerZero messaging fee from the send() operation.
     *
     * @dev MessagingFee: LayerZero msg fee
     *  - nativeFee: The native fee.
     *  - lzTokenFee: The lzToken fee.
     */
    function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);

    /**
     * @notice Executes the send() operation.
     * @param _sendParam The parameters for the send operation.
     * @param _fee The fee information supplied by the caller.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess funds from fees etc. on the src.
     * @return receipt The LayerZero messaging receipt from the send() operation.
     * @return oftReceipt The OFT receipt information.
     *
     * @dev MessagingReceipt: LayerZero msg receipt
     *  - guid: The unique identifier for the sent message.
     *  - nonce: The nonce of the sent message.
     *  - fee: The LayerZero fee incurred for the message.
     */
    function send(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}

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

interface IL1MessageQueueV2 {
    function estimateCrossDomainMessageFee(uint256 _gasLimit) external view returns (uint256);
}

interface IL1GatewayRouter {
    function depositERC20(address _token, address _to, uint256 _amount, uint256 _gasLimit) external payable;
    function getERC20Gateway(address _token) external view returns (address);
}

interface IL1ERC20Gateway {
    function messenger() external view returns (address);
}

interface IL1ERC20Messenger {
    function messageQueueV2() external view returns (address);
}

interface IL2GatewayRouter {
    function withdrawERC20(address _token, address _to, uint256 _amount, uint256 _gasLimit) external payable;
    function getERC20Gateway(address _token) external view returns (address);
}

interface IL2Messenger {
    function sendMessage(address _to, uint256 _value, bytes memory _message, uint256 _gasLimit) external payable;
}

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

import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { ReentrancyGuardTransientUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardTransientUpgradeable.sol";

import { IRoleRegistry } from "../interfaces/IRoleRegistry.sol";

/**
 * @title UpgradeableProxy
 * @author ether.fi
 * @notice An UpgradeableProxy contract which can be upgraded by RoleRegistry contract
 */
contract UpgradeableProxy is UUPSUpgradeable, PausableUpgradeable, ReentrancyGuardTransientUpgradeable {
    /// @custom:storage-location erc7201:etherfi.storage.UpgradeableProxy
    struct UpgradeableProxyStorage {
        /// @notice Reference to the role registry contract for access control
        IRoleRegistry roleRegistry;
    }

    // keccak256(abi.encode(uint256(keccak256("etherfi.storage.UpgradeableProxy")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant UpgradeableProxyStorageLocation = 0xa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500;

    /// @notice Error thrown when caller is unauthorized to perform an operation
    error Unauthorized();

    /// @notice Error thrown when caller is not the role registry owner
    error OnlyRoleRegistryOwner();


    /**
     * @notice Returns the address of the Role Registry contract
     * @return roleRegistry Reference to the role registry contract
     */
    function roleRegistry() public view returns (IRoleRegistry) {
        UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
        return $.roleRegistry;
    }

    /**
     * @dev Initializes the contract with Role Registry
     * @param _roleRegistry Address of the role registry contract
     */
    function __UpgradeableProxy_init(address _roleRegistry) internal onlyInitializing {
        UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
        $.roleRegistry = IRoleRegistry(_roleRegistry);

        __ReentrancyGuardTransient_init();
        __Pausable_init_unchained();
    }

    /**
     * @dev Returns the storage struct from the specified storage slot
     * @return $ Reference to the UpgradeableProxyStorage struct
     */
    function _getUpgradeableProxyStorage() internal pure returns (UpgradeableProxyStorage storage $) {
        assembly {
            $.slot := UpgradeableProxyStorageLocation
        }
    }

    /**
     * @dev Updates the role registry contract address
     * @param _roleRegistry The address of the new role registry contract
     * @custom:security This is a critical function that updates access control
     */
    function _setRoleRegistry(address _roleRegistry) internal {
        UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
        $.roleRegistry = IRoleRegistry(_roleRegistry);
    }

    /**
     * @dev Ensures only authorized upgraders can upgrade the contract
     * @param newImplementation Address of the new implementation contract
     */
    function _authorizeUpgrade(address newImplementation) internal view override {
        UpgradeableProxyStorage storage $ = _getUpgradeableProxyStorage();
        $.roleRegistry.onlyUpgrader(msg.sender);

        // Silence compiler warning on unused variables.
        newImplementation = newImplementation;
    }

    /**
     * @notice Pauses the contract
     * @dev Only callable by accounts with the pauser role
     */
    function pause() external {
        roleRegistry().onlyPauser(msg.sender);
        _pause();
    }

    /**
     * @notice Unpauses the contract
     * @dev Only callable by accounts with the unpauser role
     */
    function unpause() external {
        roleRegistry().onlyUnpauser(msg.sender);
        _unpause();
    }

    /**
     * @dev Modifier to restrict access to specific roles
     * @param role Role identifier
     */
    modifier onlyRole(bytes32 role) {
        if (!roleRegistry().hasRole(role, msg.sender)) revert Unauthorized();
        _;
    }

    /**
     * @dev Modifier to restrict access to owner of the role registry
     */
    modifier onlyRoleRegistryOwner() {
        if (roleRegistry().owner() != msg.sender) revert OnlyRoleRegistryOwner();
        _;
    }
}

File 11 of 38 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/**
 * @title Constants
 * @author ether.fi
 * @notice Contract that defines commonly used constants across the ether.fi protocol
 * @dev This contract is not meant to be deployed but to be inherited by other contracts
 */
contract Constants {
    /**
     * @notice Special address used to represent native ETH in the protocol
     * @dev This address is used as a marker since ETH is not an ERC20 token
     */
    address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}

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

pragma solidity ^0.8.20;

/**
 * @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 v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

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
pragma solidity ^0.8.28;

/**
 * @title IRoleRegistry
 * @notice Interface for role-based access control management
 * @dev Provides functions for managing and querying role assignments
 */
interface IRoleRegistry {
    /**
     * @notice Verifies if an account has pauser privileges
     * @param account The address to check for pauser role
     * @custom:throws Reverts if account is not an authorized pauser
     */
    function onlyPauser(address account) external view;

    /**
     * @notice Verifies if an account has unpauser privileges
     * @param account The address to check for unpauser role
     * @custom:throws Reverts if account is not an authorized unpauser
     */
    function onlyUnpauser(address account) external view;

    /**
     * @notice Checks if an account has any of the specified roles
     * @dev Reverts if the account doesn't have at least one of the roles
     * @param account The address to check roles for
     * @param encodedRoles ABI encoded roles using abi.encode(ROLE_1, ROLE_2, ...)
     * @custom:throws Reverts if account has none of the specified roles
     */
    function checkRoles(address account, bytes memory encodedRoles) external view;

    /**
     * @notice Checks if an account has a specific role
     * @dev Direct query for a single role status
     * @param role The role identifier to check
     * @param account The address to check the role for
     * @return True if the account has the role, false otherwise
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @notice Grants a role to an account
     * @dev Only callable by the contract owner
     * @param role The role identifier to grant
     * @param account The address to grant the role to
     * @custom:access Restricted to contract owner
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @notice Revokes a role from an account
     * @dev Only callable by the contract owner
     * @param role The role identifier to revoke
     * @param account The address to revoke the role from
     * @custom:access Restricted to contract owner
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @notice Retrieves all addresses that have a specific role
     * @dev Wrapper around EnumerableRoles roleHolders function
     * @param role The role identifier to query
     * @return Array of addresses that have the specified role
     */
    function roleHolders(bytes32 role) external view returns (address[] memory);

    /**
     * @notice Verifies if an account has upgrader privileges
     * @dev Used for upgrade authorization checks
     * @param account The address to check for upgrader role
     * @custom:throws Reverts if account is not an authorized upgrader
     */
    function onlyUpgrader(address account) external view;

    /**
     * @notice Returns the owner of the contract
     * @return result Owner of the contract
     */
    function owner() external view returns (address result);

    /**
     * @notice Generates a unique role identifier for safe administrators
     * @dev Creates a unique bytes32 identifier by hashing the safe address with a role type
     * @param safe The address of the safe for which to generate the admin role
     * @return bytes32 A unique role identifier for the specified safe's admins
     * @custom:throws InvalidInput if safe is a zero address
     */
    function getSafeAdminRole(address safe) external pure returns (bytes32);

    /**
     * @notice Configures admin roles for a specific safe
     * @dev Grants/revokes admin privileges to specified addresses for a particular safe
     * @param accounts Array of admin addresses to configure
     * @param shouldAdd Array indicating whether to add or remove each admin
     * @custom:throws OnlyEtherFiSafe if called by any address other than a registered EtherFiSafe
     * @custom:throws InvalidInput if the admins array is empty or contains a zero address
     * @custom:throws ArrayLengthMismatch if the array lengths mismatch
     */
    function configureSafeAdmins(address[] calldata accounts, bool[] calldata shouldAdd) external;

    /**
     * @notice Verifies if an account has safe admin privileges
     * @param safe The address of the safe
     * @param account The address to check for safe admin role
     * @custom:throws OnlySafeAdmin if the account does not have the SafeAdmin role
     */
    function onlySafeAdmin(address safe, address account) external view;

    /**
     * @notice Returns if an account has safe admin privileges
     * @param safe The address of the safe
     * @param account The address to check for safe admin role
     * @return bool suggesting if the account has the safe admin role
     */
    function isSafeAdmin(address safe, address account) external view returns (bool);

    /**
     * @notice Retrieves all addresses that have the safe admin role for a particular safe
     * @param safe The address of the safe
     * @return Array of addresses that have the safe admin role
     */
    function getSafeAdmins(address safe) external view returns (address[] memory);
}

File 15 of 38 : EnumerableSetLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for managing enumerable sets in storage.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EnumerableSetLib.sol)
///
/// @dev Note:
/// In many applications, the number of elements in an enumerable set is small.
/// This enumerable set implementation avoids storing the length and indices
/// for up to 3 elements. Once the length exceeds 3 for the first time, the length
/// and indices will be initialized. The amortized cost of adding elements is O(1).
///
/// The AddressSet implementation packs the length with the 0th entry.
///
/// All enumerable sets except Uint8Set use a pop and swap mechanism to remove elements.
/// This means that the iteration order of elements can change between element removals.
library EnumerableSetLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The index must be less than the length.
    error IndexOutOfBounds();

    /// @dev The value cannot be the zero sentinel.
    error ValueIsZeroSentinel();

    /// @dev Cannot accommodate a new unique value with the capacity.
    error ExceedsCapacity();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A sentinel value to denote the zero value in storage.
    /// No elements can be equal to this value.
    /// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
    uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;

    /// @dev The storage layout is given by:
    /// ```
    ///     mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
    ///     mstore(0x00, set.slot)
    ///     let rootSlot := keccak256(0x00, 0x24)
    ///     mstore(0x20, rootSlot)
    ///     mstore(0x00, shr(96, shl(96, value)))
    ///     let positionSlot := keccak256(0x00, 0x40)
    ///     let valueSlot := add(rootSlot, sload(positionSlot))
    ///     let valueInStorage := shr(96, sload(valueSlot))
    ///     let lazyLength := shr(160, shl(160, sload(rootSlot)))
    /// ```
    uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;

    /// @dev The storage layout is given by:
    /// ```
    ///     mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
    ///     mstore(0x00, set.slot)
    ///     let rootSlot := keccak256(0x00, 0x24)
    ///     mstore(0x20, rootSlot)
    ///     mstore(0x00, value)
    ///     let positionSlot := keccak256(0x00, 0x40)
    ///     let valueSlot := add(rootSlot, sload(positionSlot))
    ///     let valueInStorage := sload(valueSlot)
    ///     let lazyLength := sload(not(rootSlot))
    /// ```
    uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An enumerable address set in storage.
    struct AddressSet {
        uint256 _spacer;
    }

    /// @dev An enumerable bytes32 set in storage.
    struct Bytes32Set {
        uint256 _spacer;
    }

    /// @dev An enumerable uint256 set in storage.
    struct Uint256Set {
        uint256 _spacer;
    }

    /// @dev An enumerable int256 set in storage.
    struct Int256Set {
        uint256 _spacer;
    }

    /// @dev An enumerable uint8 set in storage. Useful for enums.
    struct Uint8Set {
        uint256 data;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     GETTERS / SETTERS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of elements in the set.
    function length(AddressSet storage set) internal view returns (uint256 result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            let rootPacked := sload(rootSlot)
            let n := shr(160, shl(160, rootPacked))
            result := shr(1, n)
            for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
                result := 1
                if iszero(sload(add(rootSlot, result))) { break }
                result := 2
                if iszero(sload(add(rootSlot, result))) { break }
                result := 3
                break
            }
        }
    }

    /// @dev Returns the number of elements in the set.
    function length(Bytes32Set storage set) internal view returns (uint256 result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            let n := sload(not(rootSlot))
            result := shr(1, n)
            for {} iszero(n) {} {
                result := 0
                if iszero(sload(add(rootSlot, result))) { break }
                result := 1
                if iszero(sload(add(rootSlot, result))) { break }
                result := 2
                if iszero(sload(add(rootSlot, result))) { break }
                result := 3
                break
            }
        }
    }

    /// @dev Returns the number of elements in the set.
    function length(Uint256Set storage set) internal view returns (uint256 result) {
        result = length(_toBytes32Set(set));
    }

    /// @dev Returns the number of elements in the set.
    function length(Int256Set storage set) internal view returns (uint256 result) {
        result = length(_toBytes32Set(set));
    }

    /// @dev Returns the number of elements in the set.
    function length(Uint8Set storage set) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let packed := sload(set.slot) } packed { result := add(1, result) } {
                packed := xor(packed, and(packed, add(1, not(packed))))
            }
        }
    }

    /// @dev Returns whether `value` is in the set.
    function contains(AddressSet storage set, address value) internal view returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            value := shr(96, shl(96, value))
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            let rootPacked := sload(rootSlot)
            for {} 1 {} {
                if iszero(shr(160, shl(160, rootPacked))) {
                    result := 1
                    if eq(shr(96, rootPacked), value) { break }
                    if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
                    if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
                    result := 0
                    break
                }
                mstore(0x20, rootSlot)
                mstore(0x00, value)
                result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                break
            }
        }
    }

    /// @dev Returns whether `value` is in the set.
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            for {} 1 {} {
                if iszero(sload(not(rootSlot))) {
                    result := 1
                    if eq(sload(rootSlot), value) { break }
                    if eq(sload(add(rootSlot, 1)), value) { break }
                    if eq(sload(add(rootSlot, 2)), value) { break }
                    result := 0
                    break
                }
                mstore(0x20, rootSlot)
                mstore(0x00, value)
                result := iszero(iszero(sload(keccak256(0x00, 0x40))))
                break
            }
        }
    }

    /// @dev Returns whether `value` is in the set.
    function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
        result = contains(_toBytes32Set(set), bytes32(value));
    }

    /// @dev Returns whether `value` is in the set.
    function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
        result = contains(_toBytes32Set(set), bytes32(uint256(value)));
    }

    /// @dev Returns whether `value` is in the set.
    function contains(Uint8Set storage set, uint8 value) internal view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(1, shr(and(0xff, value), sload(set.slot)))
        }
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    function add(AddressSet storage set, address value) internal returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            value := shr(96, shl(96, value))
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            let rootPacked := sload(rootSlot)
            for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                mstore(0x20, rootSlot)
                if iszero(n) {
                    let v0 := shr(96, rootPacked)
                    if iszero(v0) {
                        sstore(rootSlot, shl(96, value))
                        result := 1
                        break
                    }
                    if eq(v0, value) { break }
                    let v1 := shr(96, sload(add(rootSlot, 1)))
                    if iszero(v1) {
                        sstore(add(rootSlot, 1), shl(96, value))
                        result := 1
                        break
                    }
                    if eq(v1, value) { break }
                    let v2 := shr(96, sload(add(rootSlot, 2)))
                    if iszero(v2) {
                        sstore(add(rootSlot, 2), shl(96, value))
                        result := 1
                        break
                    }
                    if eq(v2, value) { break }
                    mstore(0x00, v0)
                    sstore(keccak256(0x00, 0x40), 1)
                    mstore(0x00, v1)
                    sstore(keccak256(0x00, 0x40), 2)
                    mstore(0x00, v2)
                    sstore(keccak256(0x00, 0x40), 3)
                    rootPacked := or(rootPacked, 7)
                    n := 7
                }
                mstore(0x00, value)
                let p := keccak256(0x00, 0x40)
                if iszero(sload(p)) {
                    n := shr(1, n)
                    result := 1
                    sstore(p, add(1, n))
                    if iszero(n) {
                        sstore(rootSlot, or(3, shl(96, value)))
                        break
                    }
                    sstore(add(rootSlot, n), shl(96, value))
                    sstore(rootSlot, add(2, rootPacked))
                    break
                }
                break
            }
        }
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            for { let n := sload(not(rootSlot)) } 1 {} {
                mstore(0x20, rootSlot)
                if iszero(n) {
                    let v0 := sload(rootSlot)
                    if iszero(v0) {
                        sstore(rootSlot, value)
                        result := 1
                        break
                    }
                    if eq(v0, value) { break }
                    let v1 := sload(add(rootSlot, 1))
                    if iszero(v1) {
                        sstore(add(rootSlot, 1), value)
                        result := 1
                        break
                    }
                    if eq(v1, value) { break }
                    let v2 := sload(add(rootSlot, 2))
                    if iszero(v2) {
                        sstore(add(rootSlot, 2), value)
                        result := 1
                        break
                    }
                    if eq(v2, value) { break }
                    mstore(0x00, v0)
                    sstore(keccak256(0x00, 0x40), 1)
                    mstore(0x00, v1)
                    sstore(keccak256(0x00, 0x40), 2)
                    mstore(0x00, v2)
                    sstore(keccak256(0x00, 0x40), 3)
                    n := 7
                }
                mstore(0x00, value)
                let p := keccak256(0x00, 0x40)
                if iszero(sload(p)) {
                    n := shr(1, n)
                    sstore(add(rootSlot, n), value)
                    sstore(p, add(1, n))
                    sstore(not(rootSlot), or(1, shl(1, add(1, n))))
                    result := 1
                    break
                }
                break
            }
        }
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
        result = add(_toBytes32Set(set), bytes32(value));
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    function add(Int256Set storage set, int256 value) internal returns (bool result) {
        result = add(_toBytes32Set(set), bytes32(uint256(value)));
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    function add(Uint8Set storage set, uint8 value) internal returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(set.slot)
            let mask := shl(and(0xff, value), 1)
            sstore(set.slot, or(result, mask))
            result := iszero(and(result, mask))
        }
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    /// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
    function add(AddressSet storage set, address value, uint256 cap)
        internal
        returns (bool result)
    {
        if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    /// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
    function add(Bytes32Set storage set, bytes32 value, uint256 cap)
        internal
        returns (bool result)
    {
        if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    /// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
    function add(Uint256Set storage set, uint256 value, uint256 cap)
        internal
        returns (bool result)
    {
        if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    /// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
    function add(Int256Set storage set, int256 value, uint256 cap) internal returns (bool result) {
        if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
    }

    /// @dev Adds `value` to the set. Returns whether `value` was not in the set.
    /// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
    function add(Uint8Set storage set, uint8 value, uint256 cap) internal returns (bool result) {
        if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
    }

    /// @dev Removes `value` from the set. Returns whether `value` was in the set.
    function remove(AddressSet storage set, address value) internal returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            value := shr(96, shl(96, value))
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            let rootPacked := sload(rootSlot)
            for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
                if iszero(n) {
                    result := 1
                    if eq(shr(96, rootPacked), value) {
                        sstore(rootSlot, sload(add(rootSlot, 1)))
                        sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    if eq(shr(96, sload(add(rootSlot, 1))), value) {
                        sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    if eq(shr(96, sload(add(rootSlot, 2))), value) {
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    result := 0
                    break
                }
                mstore(0x20, rootSlot)
                mstore(0x00, value)
                let p := keccak256(0x00, 0x40)
                let position := sload(p)
                if iszero(position) { break }
                n := sub(shr(1, n), 1)
                if iszero(eq(sub(position, 1), n)) {
                    let lastValue := shr(96, sload(add(rootSlot, n)))
                    sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
                    mstore(0x00, lastValue)
                    sstore(keccak256(0x00, 0x40), position)
                }
                sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
                sstore(p, 0)
                result := 1
                break
            }
        }
    }

    /// @dev Removes `value` from the set. Returns whether `value` was in the set.
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            if eq(value, _ZERO_SENTINEL) {
                mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
                revert(0x1c, 0x04)
            }
            if iszero(value) { value := _ZERO_SENTINEL }
            for { let n := sload(not(rootSlot)) } 1 {} {
                if iszero(n) {
                    result := 1
                    if eq(sload(rootSlot), value) {
                        sstore(rootSlot, sload(add(rootSlot, 1)))
                        sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    if eq(sload(add(rootSlot, 1)), value) {
                        sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    if eq(sload(add(rootSlot, 2)), value) {
                        sstore(add(rootSlot, 2), 0)
                        break
                    }
                    result := 0
                    break
                }
                mstore(0x20, rootSlot)
                mstore(0x00, value)
                let p := keccak256(0x00, 0x40)
                let position := sload(p)
                if iszero(position) { break }
                n := sub(shr(1, n), 1)
                if iszero(eq(sub(position, 1), n)) {
                    let lastValue := sload(add(rootSlot, n))
                    sstore(add(rootSlot, sub(position, 1)), lastValue)
                    mstore(0x00, lastValue)
                    sstore(keccak256(0x00, 0x40), position)
                }
                sstore(not(rootSlot), or(shl(1, n), 1))
                sstore(p, 0)
                result := 1
                break
            }
        }
    }

    /// @dev Removes `value` from the set. Returns whether `value` was in the set.
    function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
        result = remove(_toBytes32Set(set), bytes32(value));
    }

    /// @dev Removes `value` from the set. Returns whether `value` was in the set.
    function remove(Int256Set storage set, int256 value) internal returns (bool result) {
        result = remove(_toBytes32Set(set), bytes32(uint256(value)));
    }

    /// @dev Removes `value` from the set. Returns whether `value` was in the set.
    function remove(Uint8Set storage set, uint8 value) internal returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(set.slot)
            let mask := shl(and(0xff, value), 1)
            sstore(set.slot, and(result, not(mask)))
            result := iszero(iszero(and(result, mask)))
        }
    }

    /// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
    function update(AddressSet storage set, address value, bool isAdd, uint256 cap)
        internal
        returns (bool)
    {
        return isAdd ? add(set, value, cap) : remove(set, value);
    }

    /// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
    function update(Bytes32Set storage set, bytes32 value, bool isAdd, uint256 cap)
        internal
        returns (bool)
    {
        return isAdd ? add(set, value, cap) : remove(set, value);
    }

    /// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
    function update(Uint256Set storage set, uint256 value, bool isAdd, uint256 cap)
        internal
        returns (bool)
    {
        return isAdd ? add(set, value, cap) : remove(set, value);
    }

    /// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
    function update(Int256Set storage set, int256 value, bool isAdd, uint256 cap)
        internal
        returns (bool)
    {
        return isAdd ? add(set, value, cap) : remove(set, value);
    }

    /// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
    function update(Uint8Set storage set, uint8 value, bool isAdd, uint256 cap)
        internal
        returns (bool)
    {
        return isAdd ? add(set, value, cap) : remove(set, value);
    }

    /// @dev Returns all of the values in the set.
    /// Note: This can consume more gas than the block gas limit for large sets.
    function values(AddressSet storage set) internal view returns (address[] memory result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            let zs := _ZERO_SENTINEL
            let rootPacked := sload(rootSlot)
            let n := shr(160, shl(160, rootPacked))
            result := mload(0x40)
            let o := add(0x20, result)
            let v := shr(96, rootPacked)
            mstore(o, mul(v, iszero(eq(v, zs))))
            for {} 1 {} {
                if iszero(n) {
                    if v {
                        n := 1
                        v := shr(96, sload(add(rootSlot, n)))
                        if v {
                            n := 2
                            mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                            v := shr(96, sload(add(rootSlot, n)))
                            if v {
                                n := 3
                                mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                            }
                        }
                    }
                    break
                }
                n := shr(1, n)
                for { let i := 1 } lt(i, n) { i := add(i, 1) } {
                    v := shr(96, sload(add(rootSlot, i)))
                    mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                }
                break
            }
            mstore(result, n)
            mstore(0x40, add(o, shl(5, n)))
        }
    }

    /// @dev Returns all of the values in the set.
    /// Note: This can consume more gas than the block gas limit for large sets.
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            let zs := _ZERO_SENTINEL
            let n := sload(not(rootSlot))
            result := mload(0x40)
            let o := add(0x20, result)
            for {} 1 {} {
                if iszero(n) {
                    let v := sload(rootSlot)
                    if v {
                        n := 1
                        mstore(o, mul(v, iszero(eq(v, zs))))
                        v := sload(add(rootSlot, n))
                        if v {
                            n := 2
                            mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
                            v := sload(add(rootSlot, n))
                            if v {
                                n := 3
                                mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
                            }
                        }
                    }
                    break
                }
                n := shr(1, n)
                for { let i := 0 } lt(i, n) { i := add(i, 1) } {
                    let v := sload(add(rootSlot, i))
                    mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
                }
                break
            }
            mstore(result, n)
            mstore(0x40, add(o, shl(5, n)))
        }
    }

    /// @dev Returns all of the values in the set.
    /// Note: This can consume more gas than the block gas limit for large sets.
    function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
        result = _toUints(values(_toBytes32Set(set)));
    }

    /// @dev Returns all of the values in the set.
    /// Note: This can consume more gas than the block gas limit for large sets.
    function values(Int256Set storage set) internal view returns (int256[] memory result) {
        result = _toInts(values(_toBytes32Set(set)));
    }

    /// @dev Returns all of the values in the set.
    function values(Uint8Set storage set) internal view returns (uint8[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let ptr := add(result, 0x20)
            let o := 0
            for { let packed := sload(set.slot) } packed {} {
                if iszero(and(packed, 0xffff)) {
                    o := add(o, 16)
                    packed := shr(16, packed)
                    continue
                }
                mstore(ptr, o)
                ptr := add(ptr, shl(5, and(packed, 1)))
                o := add(o, 1)
                packed := shr(1, packed)
            }
            mstore(result, shr(5, sub(ptr, add(result, 0x20))))
            mstore(0x40, ptr)
        }
    }

    /// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
    function at(AddressSet storage set, uint256 i) internal view returns (address result) {
        bytes32 rootSlot = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            result := shr(96, sload(add(rootSlot, i)))
            result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
        }
        if (i >= length(set)) revert IndexOutOfBounds();
    }

    /// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
    function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
        result = _rootSlot(set);
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(add(result, i))
            result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
        }
        if (i >= length(set)) revert IndexOutOfBounds();
    }

    /// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
    function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
        result = uint256(at(_toBytes32Set(set), i));
    }

    /// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
    function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
        result = int256(uint256(at(_toBytes32Set(set), i)));
    }

    /// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
    function at(Uint8Set storage set, uint256 i) internal view returns (uint8 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let packed := sload(set.slot)
            for {} 1 {
                mstore(0x00, 0x4e23d035) // `IndexOutOfBounds()`.
                revert(0x1c, 0x04)
            } {
                if iszero(lt(i, 256)) { continue }
                for { let j := 0 } iszero(eq(i, j)) {} {
                    packed := xor(packed, and(packed, add(1, not(packed))))
                    j := add(j, 1)
                }
                if iszero(packed) { continue }
                break
            }
            // Find first set subroutine, optimized for smaller bytecode size.
            let x := and(packed, add(1, not(packed)))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            // For the lower 5 bits of the result, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            result := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the root slot.
    function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
            mstore(0x00, s.slot)
            r := keccak256(0x00, 0x24)
        }
    }

    /// @dev Returns the root slot.
    function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
            mstore(0x00, s.slot)
            r := keccak256(0x00, 0x24)
        }
    }

    /// @dev Casts to a Bytes32Set.
    function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
        /// @solidity memory-safe-assembly
        assembly {
            c.slot := s.slot
        }
    }

    /// @dev Casts to a Bytes32Set.
    function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
        /// @solidity memory-safe-assembly
        assembly {
            c.slot := s.slot
        }
    }

    /// @dev Casts to a uint256 array.
    function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := a
        }
    }

    /// @dev Casts to a int256 array.
    function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := a
        }
    }
}

File 16 of 38 : IDebtManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { IEtherFiDataProvider } from "./IEtherFiDataProvider.sol";
import { BinSponsor } from "./ICashModule.sol";

interface IDebtManager {
    struct BorrowTokenConfigData {
        uint64 borrowApy;
        uint128 minShares;
    }

    struct BorrowTokenConfig {
        uint256 interestIndexSnapshot;
        uint256 totalBorrowingAmount;
        uint256 totalSharesOfBorrowTokens;
        uint64 lastUpdateTimestamp;
        uint64 borrowApy;
        uint128 minShares;
    }

    struct CollateralTokenConfig {
        uint80 ltv;
        uint80 liquidationThreshold;
        uint96 liquidationBonus;
    }

    struct TokenData {
        address token;
        uint256 amount;
    }

    struct LiquidationTokenData {
        address token;
        uint256 amount;
        uint256 liquidationBonus;
    }

    event Supplied(address indexed sender, address indexed user, address indexed token, uint256 amount);
    event Borrowed(address indexed user, address indexed token, uint256 amount);
    event Repaid(address indexed user, address indexed payer, address indexed token, uint256 amount);
    event Liquidated(address indexed liquidator, address indexed user, address indexed debtTokenToLiquidate, LiquidationTokenData[] userCollateralLiquidated, uint256 beforeDebtAmount, uint256 debtAmountLiquidated);
    event LiquidationThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
    event CollateralTokenAdded(address token);
    event CollateralTokenRemoved(address token);
    event BorrowTokenAdded(address token);
    event BorrowTokenRemoved(address token);
    event BorrowApySet(address indexed token, uint256 oldApy, uint256 newApy);
    event MinSharesOfBorrowTokenSet(address indexed token, uint128 oldMinShares, uint128 newMinShares);
    event UserInterestAdded(address indexed user, uint256 borrowingAmtBeforeInterest, uint256 borrowingAmtAfterInterest);
    event TotalBorrowingUpdated(address indexed borrowToken, uint256 totalBorrowingAmtBeforeInterest, uint256 totalBorrowingAmtAfterInterest);
    event BorrowTokenConfigSet(address indexed token, BorrowTokenConfig config);
    event CollateralTokenConfigSet(address indexed collateralToken, CollateralTokenConfig oldConfig, CollateralTokenConfig newConfig);
    event WithdrawBorrowToken(address indexed withdrawer, address indexed borrowToken, uint256 amount);
    event InterestIndexUpdated(address indexed borrowToken, uint256 oldIndex, uint256 newIndex);

    error CollateralPreferenceIsEmpty();
    error UnsupportedCollateralToken();
    error UnsupportedRepayToken();
    error UnsupportedBorrowToken();
    error InsufficientCollateral();
    error InsufficientCollateralToRepay();
    error InsufficientLiquidity();
    error CannotLiquidateYet();
    error ZeroCollateralValue();
    error OnlyUserCanRepayWithCollateral();
    error InvalidValue();
    error AlreadyCollateralToken();
    error AlreadyBorrowToken();
    error NotACollateralToken();
    error NoCollateralTokenLeft();
    error NotABorrowToken();
    error NoBorrowTokenLeft();
    error ArrayLengthMismatch();
    error TotalCollateralAmountNotZero();
    error InsufficientLiquidityPleaseTryAgainLater();
    error LiquidAmountLesserThanRequired();
    error ZeroTotalBorrowTokens();
    error InsufficientBorrowShares();
    error UserStillLiquidatable();
    error TotalBorrowingsForUserNotZero();
    error BorrowTokenConfigAlreadySet();
    error AccountUnhealthy();
    error BorrowTokenStillInTheSystem();
    error RepaymentAmountIsZero();
    error LiquidatableAmountIsZero();
    error LtvCannotBeGreaterThanLiquidationThreshold();
    error OraclePriceZero();
    error BorrowAmountZero();
    error SharesCannotBeZero();
    error SharesCannotBeLessThanMinShares();
    error SupplyCapBreached();
    error OnlyEtherFiSafe();
    error EtherFiSafeCannotSupplyDebtTokens();
    error BorrowTokenCannotBeRemovedFromCollateral();

    /**
     * @notice Function to fetch the admin role
     * @return DEBT_MANAGER_ADMIN_ROLE
     */
    function DEBT_MANAGER_ADMIN_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the max borrow apy
     * @return Max borrow APY
     */
    function MAX_BORROW_APY() external view returns (uint64);

    /**
     * @notice Returns the address of DebtManagerAdmin implementation
     * @return address DebtManagerAdmin implmentaion
     */
    function getDebtManagerAdmin() external view returns (address);

    /**
     * @notice Returns an instance of the EtherFiDataProvider 
     * @return EtherFiDataProvider instance
     */
    function etherFiDataProvider() external view returns (IEtherFiDataProvider);

    /**
     * @notice Function to fetch the address of the Cash Data Provider.
     * @return Cash Data Provider address
     */
    function cashDataProvider() external view returns (address);

    /**
     * @notice Function to fetch the debt interest index snapshot.
     * @param  borrowToken Address of the borrow token.
     * @return debt interest index snapshot
     */
    function debtInterestIndexSnapshot(address borrowToken) external view returns (uint256);

    /**
     * @notice Function to fetch the borrow APY per second with 18 decimals.
     * @param borrowToken Address of the borrow token.
     * @return Borrow APY per second. Eg: 0.0001% -> 0.0001e18
     */
    function borrowApyPerSecond(address borrowToken) external view returns (uint64);

    /**
     * @notice Function to fetch the min shares of borrow token that can be minted by a supplier.
     * @param borrowToken Address of the borrow token.
     * @return minShares
     */
    function borrowTokenMinShares(address borrowToken) external view returns (uint128);

    /**
     * @notice Function to fetch the array of collateral tokens.
     * @return Array of collateral tokens.
     */
    function getCollateralTokens() external view returns (address[] memory);

    /**
     * @notice Function to fetch the array of borrow tokens.
     * @return Array of borrow tokens.
     */
    function getBorrowTokens() external view returns (address[] memory);

    /**
     * @notice Function to check whether a token is a collateral token.
     * @return Boolean value suggesting if token is a collateral token.
     */
    function isCollateralToken(address token) external view returns (bool);

    /**
     * @notice Function to check whether a token is a borrow token.
     * @return Boolean value suggesting if token is a borrow token.
     */
    function isBorrowToken(address token) external view returns (bool);

    /**
     * @notice Function to add support for a new collateral token.
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
     * @param token Address of the token to be supported as collateral.
     * @param config Collateral token config.
     */
    function supportCollateralToken(address token, CollateralTokenConfig memory config) external;

    /**
     * @notice Function to set the borrow APY per second for a borrow token.
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
     * @param token Address of the borrow token.
     * @param apy Borrow APY per seconds with 18 decimals.
     */
    function setBorrowApy(address token, uint64 apy) external;

    /**
     * @notice Function to set min borrow token shares to mint.
     * @notice Implemented to prevent inflation attacks.
     * @param token Address of the borrow token.
     * @param shares Min shares of that borrow token to mint.
     */
    function setMinBorrowTokenShares(address token, uint128 shares) external;

    /**
     * @notice Function to set the collateral token config.
     * @param __collateralToken Address of the collateral token.
     * @param __config Collateral token config.
     */
    function setCollateralTokenConfig(address __collateralToken, CollateralTokenConfig memory __config) external;

    /**
     * @notice Function to remove support for a collateral token.
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
     * @param token Address of the token to be unsupported as collateral.
     */
    function unsupportCollateralToken(address token) external;

    /**
     * @notice Function to add support for a new borrow token.
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE. 
     * @param token Address of the token to be supported as borrow.
     * @param borrowApy Borrow APY per second in 18 decimals.
     */
    function supportBorrowToken(address token, uint64 borrowApy, uint128 minBorrowTokenShares) external;

    /**
     * @notice Function to remove support for a borrow token.
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
     * @param token Address of the token to be unsupported as borrow.
     */
    function unsupportBorrowToken(address token) external;

    /**
     * @notice Function to ensure that the debt position of the user safe is healthy
     * @param user Address of the user safe
     */
    function ensureHealth(address user) external view;

    /**
     * @notice Function to supply borrow tokens to the debt manager.
     * @param  user Address of the user to register for supply.
     * @param  borrowToken Address of the borrow token to supply.
     * @param  amount Amount of the borrow token to supply.
     */
    function supply(address user, address borrowToken, uint256 amount) external;

    /**
     * @notice Function to withdraw the borrow tokens.
     * @param  borrowToken Address of the borrow token.
     * @param  amount Amount of the borrow token to withdraw.
     */
    function withdrawBorrowToken(address borrowToken, uint256 amount) external;

    /**
     * @notice Function for users to borrow funds for payment using the deposited collateral.
     * @notice Borrowed tokens are transferred to the `etherFiCashSafe`
     * @param  binSponsor Bin sponsor used to spend.
     * @param  token Address of the token to borrow.
     * @param  amount Amount of the token to borrow.
     */
    function borrow(BinSponsor binSponsor, address token, uint256 amount) external;

    /**
     * @notice Function for users to repay the borrowed funds back to the debt manager.
     * @param  user Address of the user safe for whom the payment is made.
     * @param  token Address of the token in which repayment is done.
     * @param  amount Amount of tokens to be repaid.
     */
    function repay(address user, address token, uint256 amount) external;

    // https://docs.aave.com/faq/liquidations
    /**
     * @notice Liquidate the user's debt by repaying the partial/entire debt using the collateral.
     * @notice If user's 50% debt is repaid and user is healthy, then only 50% will be repaid. Otherwise entire debt is repaid.
     * @dev do we need to add penalty?
     * @param  user Address of the user to liquidate.
     * @param  borrowToken Borrow token address to liquidate.
     * @param  collateralTokensPreference Preference of order of collateral tokens to liquidate the user for.
     */
    function liquidate(address user, address borrowToken, address[] memory collateralTokensPreference) external;

    /**
     * @notice Function to determine if a user is liquidatable.
     * @param  user Address of the user.
     * @return isLiquidatable boolean value.
     */
    function liquidatable(address user) external view returns (bool isLiquidatable);

    /**
     * @notice Function to fetch the collateral amount for the user.
     * @param  user Address of the user.
     * @return Array of TokenData struct, total collateral amount in usd.
     */
    function collateralOf(address user) external view returns (TokenData[] memory, uint256);

    /**
     * @notice Function to fetch the borrowing amount of the user for a borrow token.
     * @param  user Address of the user.
     * @param  borrowToken Address of the borrow token.
     * @return Borrow amount with interest.
     */
    function borrowingOf(address user, address borrowToken) external view returns (uint256);

    /**
     * @notice Function to fetch the borrowing amount of the user for a all the borrow tokens.
     * @param  user Address of the user.
     * @return Array of TokenData struct, total borrow amount in usd.
     */
    function borrowingOf(address user) external view returns (TokenData[] memory, uint256);

    /**
     * @notice Function to fetch the max borrow amount for ltv or liquidation purpose.
     * @notice Calculates user's total collateral amount in USD and finds max borrowable amount using liquidation threshold.
     * @param  user Address of the user.
     * @param  forLtv For ltv, pass true and for liquidation, pass false.
     * @return Max borrow amount for liquidation purpose.
     */
    function getMaxBorrowAmount(address user, bool forLtv) external view returns (uint256);

    /**
     * @notice Function to fetch the max borrow and total current borrowings
     * @param user Address of the user safe
     * @param tokenAmounts Token amounts of collateral
     * @return Total max borrow for that user
     * @return Current total borrowings of the user
     */
    function getBorrowingPowerAndTotalBorrowing(address user, TokenData[] memory tokenAmounts) external view returns (uint256, uint256);

    /**
     * @notice Function to determine the current borrowable amount in USD for a user.
     * @param  user Address of the user.
     * @return Current borrowable amount for the user.
     */
    function remainingBorrowingCapacityInUSD(address user) external view returns (uint256);

    /**
     * @notice Function to get the withdrawable amount of borrow tokens for a supplier.
     * @param  supplier Address of the supplier.
     * @param  borrowToken Address of the borrow token.
     * @return Amount of borrow tokens the supplier can withdraw.
     */
    function supplierBalance(address supplier, address borrowToken) external view returns (uint256);

    /**
     * @notice Function to get the withdrawable amount of borrow tokens for a supplier.
     * @param  supplier Address of the supplier.
     * @return Array of borrow tokens addresses and respective amounts.
     * @return Total amount in USD.
     */
    function supplierBalance(address supplier) external view returns (TokenData[] memory, uint256);

    /**
     * @notice Function to fetch the total supplies for a borrow token.
     * @param borrowToken Address of the borrow token.
     * @return Total amount supplied.
     */
    function totalSupplies(address borrowToken) external view returns (uint256);

    /**
     * @notice Function to fetch the total supplies for each borrow token.
     * @return Total amount supplied for each borrow token.
     * @return Total amount supplied in USD combined.
     */
    function totalSupplies() external view returns (TokenData[] memory, uint256);

    /**
     * @notice Function to convert collateral token amount to equivalent USD amount.
     * @param  collateralToken Address of collateral to convert.
     * @param  collateralAmount Amount of collateral token to convert.
     * @return Equivalent USD amount.
     */
    function convertCollateralTokenToUsd(address collateralToken, uint256 collateralAmount) external view returns (uint256);

    /**
     * @notice Function to convert usd amount to collateral token amount.
     * @param  collateralToken Address of the collateral token.
     * @param  debtUsdAmount Amount of USD for borrowing.
     * @return Amount of collateral required.
     */
    function convertUsdToCollateralToken(address collateralToken, uint256 debtUsdAmount) external view returns (uint256);

    /**
     * @notice Function to fetch the value of collateral deposited by the user in USD.
     * @param  user Address of the user.
     * @return Total collateral value in USD for the user.
     */
    function getCollateralValueInUsd(address user) external view returns (uint256);

    /**
     * @notice Function to fetch the user collateral for a particular token.
     * @param  user Address of the user.
     * @param  token Address of the token.
     * @return Amount of collateral in tokens.
     * @return Amount of collateral in USD.
     */
    function getUserCollateralForToken(address user, address token) external view returns (uint256, uint256);

    /**
     * @notice Function to fetch the total borrowing amount for a token from this contract.
     * @param  borrowToken Address of the borrow token.
     * @return Total borrowing amount in debt token with 6 decimals.
     */
    function totalBorrowingAmount(address borrowToken) external view returns (uint256);

    /**
     * @notice Function to fetch the total borrowing amounts from this contract.
     * @return Array of borrow tokens with respective amount in USD.
     * @return Total borrowing amount in USD.
     */
    function totalBorrowingAmounts() external view returns (TokenData[] memory, uint256);

    /**
     * @notice Function to fetch the borrow token config.
     * @param  borrowToken Address of the borrow token.
     * @return BorrowTokenConfig struct.
     */
    function borrowTokenConfig(address borrowToken) external view returns (BorrowTokenConfig memory);

    /**
     * @notice Function to fetch the collateral token config.
     * @param  collateralToken Address of the collateral token.
     * @return CollateralTokenConfig.
     */
    function collateralTokenConfig(address collateralToken) external view returns (CollateralTokenConfig memory);

    /**
     * @notice Function to fetch the current state of collaterals and borrowings.
     * @return borrowings Array of borrowings in tuple(address token, uint256 amount) format.
     * @return totalBorrowingsInUsd Total borrowing value in USD.
     * @return totalLiquidStableAmounts Total liquid stable amounts in tuple(address token, uint256 amount) format.
     */
    function getCurrentState() external view returns (TokenData[] memory borrowings, uint256 totalBorrowingsInUsd, TokenData[] memory totalLiquidStableAmounts);

    /**
     * @notice Function to fetch the current state of a user.
     * @return totalCollaterals Array of collaterals in tuple(address token, uint256 amount) format.
     * @return totalCollateralInUsd Total collateral value in USD.
     * @return borrowings Array of borrowings in tuple(address token, uint256 amount) format.
     * @return totalBorrowings Total borrowing value in USD.
     */
    function getUserCurrentState(address user) external view returns (TokenData[] memory totalCollaterals, uint256 totalCollateralInUsd, TokenData[] memory borrowings, uint256 totalBorrowings);

    /**
     * @notice Sets a new DebtManagerAdmin implementation
     * @dev Can only be called by an address with the DEBT_MANAGER_ADMIN_ROLE.
     * @param newImpl Address of the new DebtManagerAdmin implementation
     */
    function setAdminImpl(address newImpl) external;

    /**
     * @notice Calculates the current interest index for a borrow token
     * @dev Computes accrued interest based on time elapsed since last update
     * @param borrowToken Address of the borrow token
     * @return The current interest index including all accrued interest
     */
    function getCurrentIndex(address borrowToken) external view returns (uint256);
}

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

interface IPriceProvider {
    error UnknownToken();
    /**
     * @notice Function to get the price of a token in USD
     * @return Price with 6 decimals
     */

    function price(address token) external view returns (uint256);

    /**
     * @notice Function to fetch the admin role
     * @return PRICE_PROVIDER_ADMIN_ROLE
     */
    function PRICE_PROVIDER_ADMIN_ROLE() external view returns (bytes32);
}

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

import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

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

/**
 * @title SpendingLimit
 * @notice Data structure for managing daily and monthly spending limits with time-based renewals
 * @dev Includes current limits, spent amounts, pending limit changes, and renewal timestamps
 */
struct SpendingLimit {
    uint256 dailyLimit; // in USD with 6 decimals
    uint256 monthlyLimit; // in USD with 6 decimals
    uint256 spentToday; // in USD with 6 decimals
    uint256 spentThisMonth; // in USD with 6 decimals
    uint256 newDailyLimit; // in USD with 6 decimals
    uint256 newMonthlyLimit; // in USD with 6 decimals
    uint64 dailyRenewalTimestamp;
    uint64 monthlyRenewalTimestamp;
    uint64 dailyLimitChangeActivationTime;
    uint64 monthlyLimitChangeActivationTime;
    int256 timezoneOffset;
}

/**
 * @title SpendingLimitLib
 * @notice Library for managing spending limits with daily and monthly caps
 * @dev Provides functionality for initializing, updating, and enforcing spending limits
 * @author ether.fi
 */
library SpendingLimitLib {
    using TimeLib for uint256;
    using Math for uint256;

    /**
     * @notice Error thrown when a spend would exceed the daily limit
     */
    error ExceededDailySpendingLimit();

    /**
     * @notice Error thrown when a spend would exceed the monthly limit
     */
    error ExceededMonthlySpendingLimit();

    /**
     * @notice Error thrown when daily limit is set higher than monthly limit
     */
    error DailyLimitCannotBeGreaterThanMonthlyLimit();

    /**
     * @notice Error thrown when timezone offset is invalid
     */
    error InvalidTimezoneOffset();

    /**
     * @notice Initializes a new SpendingLimit with daily and monthly caps
     * @dev Sets up initial renewal timestamps based on current time and timezone offset
     * @param limit Storage reference to the SpendingLimit to initialize
     * @param dailyLimit Maximum amount that can be spent in a day (USD with 6 decimals)
     * @param monthlyLimit Maximum amount that can be spent in a month (USD with 6 decimals)
     * @param timezoneOffset User's timezone offset in seconds
     * @return The initialized SpendingLimit
     * @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
     * @custom:throws InvalidTimezoneOffset if timezone offset is outside valid range
     */
    function initialize(SpendingLimit storage limit, uint256 dailyLimit, uint256 monthlyLimit, int256 timezoneOffset) internal sanity(dailyLimit, monthlyLimit) returns (SpendingLimit memory) {
        if (timezoneOffset > 24 * 60 * 60 || timezoneOffset < -24 * 60 * 60) revert InvalidTimezoneOffset();
        limit.dailyLimit = dailyLimit;
        limit.monthlyLimit = monthlyLimit;
        limit.timezoneOffset = timezoneOffset;
        limit.dailyRenewalTimestamp = block.timestamp.getStartOfNextDay(limit.timezoneOffset);
        limit.monthlyRenewalTimestamp = block.timestamp.getStartOfNextMonth(limit.timezoneOffset);

        return limit;
    }

    /**
     * @notice Updates storage with the current limit state
     * @dev Refreshes all fields of the limit struct with current values
     * @param limit Storage reference to the SpendingLimit to update
     */
    function currentLimit(SpendingLimit storage limit) internal {
        SpendingLimit memory finalLimit = getCurrentLimit(limit);

        limit.dailyLimit = finalLimit.dailyLimit;
        limit.monthlyLimit = finalLimit.monthlyLimit;
        limit.spentToday = finalLimit.spentToday;
        limit.spentThisMonth = finalLimit.spentThisMonth;
        limit.newDailyLimit = finalLimit.newDailyLimit;
        limit.newMonthlyLimit = finalLimit.newMonthlyLimit;
        limit.dailyRenewalTimestamp = finalLimit.dailyRenewalTimestamp;
        limit.monthlyRenewalTimestamp = finalLimit.monthlyRenewalTimestamp;
        limit.dailyLimitChangeActivationTime = finalLimit.dailyLimitChangeActivationTime;
        limit.monthlyLimitChangeActivationTime = finalLimit.monthlyLimitChangeActivationTime;
    }

    /**
     * @notice Records a spend against the daily and monthly limits
     * @dev Updates current limits first, then applies the spend if within limits
     * @param limit Storage reference to the SpendingLimit
     * @param amount Amount to spend (USD with 6 decimals)
     * @custom:throws ExceededDailySpendingLimit if spend would exceed daily limit
     * @custom:throws ExceededMonthlySpendingLimit if spend would exceed monthly limit
     */
    function spend(SpendingLimit storage limit, uint256 amount) internal {
        currentLimit(limit);

        if (limit.spentToday + amount > limit.dailyLimit) revert ExceededDailySpendingLimit();
        if (limit.spentThisMonth + amount > limit.monthlyLimit) revert ExceededMonthlySpendingLimit();

        limit.spentToday += amount;
        limit.spentThisMonth += amount;
    }

    /**
     * @notice Updates spending limits with optional delay for decreases
     * @dev Immediate increases, delayed decreases with activation timestamp
     * @param limit Storage reference to the SpendingLimit
     * @param newDailyLimit New daily spending limit (USD with 6 decimals)
     * @param newMonthlyLimit New monthly spending limit (USD with 6 decimals)
     * @param delay Seconds to delay limit decreases (0 for immediate)
     * @return Original limit before changes
     * @return Updated limit after changes
     * @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
     */
    function updateSpendingLimit(SpendingLimit storage limit, uint256 newDailyLimit, uint256 newMonthlyLimit, uint64 delay) internal sanity(newDailyLimit, newMonthlyLimit) returns (SpendingLimit memory, SpendingLimit memory) {
        currentLimit(limit);
        SpendingLimit memory oldLimit = limit;

        if (newDailyLimit < limit.dailyLimit) {
            limit.newDailyLimit = newDailyLimit;
            limit.dailyLimitChangeActivationTime = uint64(block.timestamp) + delay;
        } else {
            limit.dailyLimit = newDailyLimit;
            limit.newDailyLimit = 0;
            limit.dailyLimitChangeActivationTime = 0;
        }

        if (newMonthlyLimit < limit.monthlyLimit) {
            limit.newMonthlyLimit = newMonthlyLimit;
            limit.monthlyLimitChangeActivationTime = uint64(block.timestamp) + delay;
        } else {
            limit.monthlyLimit = newMonthlyLimit;
            limit.newMonthlyLimit = 0;
            limit.monthlyLimitChangeActivationTime = 0;
        }

        return (oldLimit, limit);
    }

    /**
     * @notice Calculates the maximum amount that can be spent right now
     * @dev Considers both daily and monthly limits, returning the lower of the two remaining amounts
     * @param limit Memory copy of the SpendingLimit
     * @return Maximum spendable amount in USD with 6 decimals
     */
    function maxCanSpend(SpendingLimit memory limit) internal view returns (uint256) {
        limit = getCurrentLimit(limit);
        bool usingIncomingDailyLimit = false;
        bool usingIncomingMonthlyLimit = false;
        uint256 applicableDailyLimit = limit.dailyLimit;
        uint256 applicableMonthlyLimit = limit.monthlyLimit;

        if (limit.dailyLimitChangeActivationTime != 0) {
            applicableDailyLimit = limit.newDailyLimit;
            usingIncomingDailyLimit = true;
        }
        if (limit.monthlyLimitChangeActivationTime != 0) {
            applicableMonthlyLimit = limit.newMonthlyLimit;
            usingIncomingMonthlyLimit = true;
        }

        if (limit.spentToday > applicableDailyLimit) return 0;
        if (limit.spentThisMonth > applicableMonthlyLimit) return 0;

        return Math.min(applicableDailyLimit - limit.spentToday, applicableMonthlyLimit - limit.spentThisMonth);
    }

    /**
     * @notice Checks if a specific amount can be spent
     * @dev Considers both daily and monthly limits, including pending limit changes
     * @param limit Memory copy of the SpendingLimit
     * @param amount Amount to check if spendable (USD with 6 decimals)
     * @return canSpend Boolean indicating if the amount can be spent
     * @return message Error message if amount cannot be spent
     */
    function canSpend(SpendingLimit memory limit, uint256 amount) internal view returns (bool, string memory) {
        limit = getCurrentLimit(limit);

        bool usingIncomingDailyLimit = false;
        bool usingIncomingMonthlyLimit = false;
        uint256 applicableDailyLimit = limit.dailyLimit;
        uint256 applicableMonthlyLimit = limit.monthlyLimit;

        if (limit.dailyLimitChangeActivationTime != 0) {
            applicableDailyLimit = limit.newDailyLimit;
            usingIncomingDailyLimit = true;
        }
        if (limit.monthlyLimitChangeActivationTime != 0) {
            applicableMonthlyLimit = limit.newMonthlyLimit;
            usingIncomingMonthlyLimit = true;
        }

        if (limit.spentToday > applicableDailyLimit) {
            if (usingIncomingDailyLimit) return (false, "Incoming daily spending limit already exhausted");
            else return (false, "Daily spending limit already exhausted");
        }

        if (limit.spentThisMonth > applicableMonthlyLimit) {
            if (usingIncomingMonthlyLimit) return (false, "Incoming monthly spending limit already exhausted");
            else return (false, "Monthly spending limit already exhausted");
        }

        uint256 availableDaily = applicableDailyLimit - limit.spentToday;
        uint256 availableMonthly = applicableMonthlyLimit - limit.spentThisMonth;

        if (amount > availableDaily) {
            if (usingIncomingDailyLimit) return (false, "Incoming daily available spending limit less than amount requested");
            return (false, "Daily available spending limit less than amount requested");
        }

        if (amount > availableMonthly) {
            if (usingIncomingMonthlyLimit) return (false, "Incoming monthly available spending limit less than amount requested");
            return (false, "Monthly available spending limit less than amount requested");
        }

        return (true, "");
    }

    /**
     * @notice Gets the current limit state with all time-based updates applied
     * @dev Applies pending limit changes and resets counters on renewal timestamps
     * @param limit Memory copy of the SpendingLimit
     * @return Updated SpendingLimit reflecting current state
     */
    function getCurrentLimit(SpendingLimit memory limit) internal view returns (SpendingLimit memory) {
        if (limit.dailyLimitChangeActivationTime != 0 && block.timestamp > limit.dailyLimitChangeActivationTime) {
            limit.dailyLimit = limit.newDailyLimit;
            limit.newDailyLimit = 0;
            limit.dailyLimitChangeActivationTime = 0;
        }

        if (limit.monthlyLimitChangeActivationTime != 0 && block.timestamp > limit.monthlyLimitChangeActivationTime) {
            limit.monthlyLimit = limit.newMonthlyLimit;
            limit.newMonthlyLimit = 0;
            limit.monthlyLimitChangeActivationTime = 0;
        }

        if (block.timestamp > limit.dailyRenewalTimestamp) {
            limit.spentToday = 0;
            limit.dailyRenewalTimestamp = getFinalDailyRenewalTimestamp(limit.dailyRenewalTimestamp, limit.timezoneOffset);
        }

        if (block.timestamp > limit.monthlyRenewalTimestamp) {
            limit.spentThisMonth = 0;
            limit.monthlyRenewalTimestamp = getFinalMonthlyRenewalTimestamp(limit.monthlyRenewalTimestamp, limit.timezoneOffset);
        }

        return limit;
    }

    /**
     * @notice Calculates the next valid daily renewal timestamp
     * @dev Handles cases where multiple renewal periods have elapsed
     * @param renewalTimestamp Current renewal timestamp
     * @param timezoneOffset User's timezone offset in seconds
     * @return Next valid daily renewal timestamp
     */
    function getFinalDailyRenewalTimestamp(uint64 renewalTimestamp, int256 timezoneOffset) internal view returns (uint64) {
        do {
            renewalTimestamp = uint256(renewalTimestamp).getStartOfNextDay(timezoneOffset);
        } while (block.timestamp > renewalTimestamp);

        return renewalTimestamp;
    }

    /**
     * @notice Calculates the next valid monthly renewal timestamp
     * @dev Handles cases where multiple renewal periods have elapsed
     * @param renewalTimestamp Current renewal timestamp
     * @param timezoneOffset User's timezone offset in seconds
     * @return Next valid monthly renewal timestamp
     */
    function getFinalMonthlyRenewalTimestamp(uint64 renewalTimestamp, int256 timezoneOffset) internal view returns (uint64) {
        do {
            renewalTimestamp = uint256(renewalTimestamp).getStartOfNextMonth(timezoneOffset);
        } while (block.timestamp > renewalTimestamp);

        return renewalTimestamp;
    }

    /**
     * @dev Modifier to ensure daily limit is not greater than monthly limit
     * @param dailyLimit Daily spending limit to validate
     * @param monthlyLimit Monthly spending limit to validate
     * @custom:throws DailyLimitCannotBeGreaterThanMonthlyLimit if daily limit exceeds monthly limit
     */
    modifier sanity(uint256 dailyLimit, uint256 monthlyLimit) {
        if (dailyLimit > monthlyLimit) revert DailyLimitCannotBeGreaterThanMonthlyLimit();
        _;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}

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

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

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

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        PausableStorage storage $ = _getPausableStorage();
        return $._paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}

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

pragma solidity ^0.8.24;

import {TransientSlot} from "@openzeppelin/contracts/utils/TransientSlot.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Variant of {ReentrancyGuard} that uses transient storage.
 *
 * NOTE: This variant only works on networks where EIP-1153 is available.
 *
 * _Available since v5.1._
 */
abstract contract ReentrancyGuardTransientUpgradeable is Initializable {
    using TransientSlot for *;

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

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

    /**
     * @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();
    }

    function __ReentrancyGuardTransient_init() internal onlyInitializing {
    }

    function __ReentrancyGuardTransient_init_unchained() internal onlyInitializing {
    }
    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
    }

    function _nonReentrantAfter() private {
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
    }

    /**
     * @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 REENTRANCY_GUARD_STORAGE.asBoolean().tload();
    }
}

File 22 of 38 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 23 of 38 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @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 {
            if (b == 0) return (false, 0);
            return (true, 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 {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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 {
            // 512-bit multiply [prod1 prod0] = 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 = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                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²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_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.

            uint256 twos = denominator & (0 - denominator);
            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²⁵⁶ / 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²⁵⁶. 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 prod1
            // is no longer required.
            result = prod0 * 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 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 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 exp;
        unchecked {
            exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
            value >>= exp;
            result += exp;

            exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
            value >>= exp;
            result += exp;

            exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
            value >>= exp;
            result += exp;

            exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
            value >>= exp;
            result += exp;

            exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
            value >>= exp;
            result += exp;

            exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
            value >>= exp;
            result += exp;

            exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
            value >>= exp;
            result += exp;

            result += SafeCast.toUint(value > 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 + 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 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 isGt;
        unchecked {
            isGt = SafeCast.toUint(value > (1 << 128) - 1);
            value >>= isGt * 128;
            result += isGt * 16;

            isGt = SafeCast.toUint(value > (1 << 64) - 1);
            value >>= isGt * 64;
            result += isGt * 8;

            isGt = SafeCast.toUint(value > (1 << 32) - 1);
            value >>= isGt * 32;
            result += isGt * 4;

            isGt = SafeCast.toUint(value > (1 << 16) - 1);
            value >>= isGt * 16;
            result += isGt * 2;

            result += SafeCast.toUint(value > (1 << 8) - 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 + 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;
    }
}

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

library TimeLib {
    // Function to find the timestamp for the start of the next day (00:00 in the user's time zone)
    function getStartOfNextDay(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
        // Adjust the timestamp for the time zone offset
        int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;

        // Calculate the current day in the adjusted time zone
        uint256 currentDay = uint256(adjustedTimestamp / 1 days);

        // Calculate the start of the next day in the adjusted time zone
        uint256 startOfNextDay = (currentDay + 1) * 1 days;

        // Adjust the result back to the user's local time zone
        return uint64(uint256(int256(startOfNextDay) - timezoneOffset));
    }

    // Function to find the start of the next week (Monday 00:00 in the user's time zone)
    function getStartOfNextWeek(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
        // Adjust the timestamp for the time zone offset
        int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;

        // Calculate the current day of the week (0 = Monday, 6 = Sunday)
        uint256 dayOfWeek = (uint256(adjustedTimestamp) / 1 days + 3) % 7;

        // Calculate the number of days until the next Monday
        uint256 daysUntilNextMonday = (dayOfWeek == 0) ? 7 : (7 - dayOfWeek);

        // Calculate the start of the next week in the adjusted time zone
        uint256 startOfNextWeek = ((uint256(adjustedTimestamp) / 1 days) + daysUntilNextMonday) * 1 days;

        // Adjust the result back to the user's local time zone
        return uint64(uint256(int256(startOfNextWeek) - timezoneOffset));
    }

    // Function to find the start of the next month (in the user's time zone)
    function getStartOfNextMonth(uint256 timestamp, int256 timezoneOffset) internal pure returns (uint64) {
        // Adjust the timestamp for the time zone offset
        int256 adjustedTimestamp = int256(timestamp) + timezoneOffset;

        // Get the current date in the adjusted time zone
        (uint16 year, uint8 month,) = _daysToDate(uint256(adjustedTimestamp) / 1 days);

        // Increment the month and adjust the year if necessary
        month += 1;
        if (month > 12) {
            month = 1;
            year += 1;
        }

        // Calculate the start of the next month in the adjusted time zone
        uint256 startOfNextMonth = _daysFromDate(year, month, 1) * 1 days;

        // Adjust the result back to the user's local time zone
        return uint64(uint256(int256(startOfNextMonth) - timezoneOffset));
    }

    // Internal function to calculate days from date
    function _daysFromDate(uint16 year, uint8 month, uint8 day) internal pure returns (uint256) {
        int256 _year = int256(uint256(year));
        int256 _month = int256(uint256(month));
        int256 _day = int256(uint256(day));

        int256 __days = _day - 32_075 + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 - 2_440_588;

        return uint256(__days);
    }

    // Internal function to convert days to date
    function _daysToDate(uint256 _days) internal pure returns (uint16 year, uint8 month, uint8 day) {
        int256 __days = int256(_days);

        int256 L = __days + 68_569 + 2_440_588;
        int256 N = 4 * L / 146_097;
        L = L - (146_097 * N + 3) / 4;
        int256 _year = 4000 * (L + 1) / 1_461_001;
        L = L - 1461 * _year / 4 + 31;
        int256 _month = 80 * L / 2447;
        int256 _day = L - 2447 * _month / 80;
        L = _month / 11;
        _month = _month + 2 - 12 * L;
        _year = 100 * (N - 49) + _year + L;

        year = uint16(uint256(_year));
        month = uint8(uint256(_month));
        day = uint8(uint256(_day));
    }
}

File 26 of 38 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.22;

import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This library provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
 */
library ERC1967Utils {
    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit IERC1967.Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit IERC1967.AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the ERC-1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit IERC1967.BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @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 Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 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 in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._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 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._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() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @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 {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

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

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

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

pragma solidity ^0.8.20;
import {Initializable} from "../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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.

pragma solidity ^0.8.24;

/**
 * @dev Library for reading and writing value-types to specific transient storage slots.
 *
 * Transient slots are often used to store temporary values that are removed after the current transaction.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 *  * Example reading and writing values using transient storage:
 * ```solidity
 * contract Lock {
 *     using TransientSlot for *;
 *
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
 *
 *     modifier locked() {
 *         require(!_LOCK_SLOT.asBoolean().tload());
 *
 *         _LOCK_SLOT.asBoolean().tstore(true);
 *         _;
 *         _LOCK_SLOT.asBoolean().tstore(false);
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library TransientSlot {
    /**
     * @dev UDVT that represent a slot holding a address.
     */
    type AddressSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a AddressSlot.
     */
    function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
        return AddressSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bool.
     */
    type BooleanSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a BooleanSlot.
     */
    function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
        return BooleanSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bytes32.
     */
    type Bytes32Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Bytes32Slot.
     */
    function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
        return Bytes32Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a uint256.
     */
    type Uint256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Uint256Slot.
     */
    function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
        return Uint256Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a int256.
     */
    type Int256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Int256Slot.
     */
    function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
        return Int256Slot.wrap(slot);
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(AddressSlot slot) internal view returns (address value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(AddressSlot slot, address value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(BooleanSlot slot) internal view returns (bool value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(BooleanSlot slot, bool value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Bytes32Slot slot, bytes32 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Uint256Slot slot) internal view returns (uint256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Uint256Slot slot, uint256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Int256Slot slot) internal view returns (int256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Int256Slot slot, int256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @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.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 33 of 38 : 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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @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.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 35 of 38 : IERC1967.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
 */
interface IERC1967 {
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) 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
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

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

File 38 of 38 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"enum BinSponsor","name":"_binSponsor","type":"uint8"},{"internalType":"address","name":"_dataProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotWithdrawZeroAmount","type":"error"},{"inputs":[],"name":"DestinationDataNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFeeToCoverCost","type":"error"},{"inputs":[],"name":"InsufficientMinReturn","type":"error"},{"inputs":[],"name":"InsufficientReturnAmount","type":"error"},{"inputs":[],"name":"InvalidBoringQueue","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"LiquidWithdrawConfigNotSet","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyRoleRegistryOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RefundWalletNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StargateValueInvalid","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"WithdrawFundsFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CanonicalBridgeWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"indexed":false,"internalType":"struct SettlementDispatcher.DestinationData[]","name":"destDatas","type":"tuple[]"}],"name":"DestinationDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint72","name":"ticketId","type":"uint72"},{"internalType":"bytes","name":"passengerBytes","type":"bytes"}],"indexed":false,"internalType":"struct Ticket","name":"ticket","type":"tuple"}],"name":"FundsBridgedWithStargate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"boringQueue","type":"address"}],"name":"LiquidWithdrawQueueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidToken","type":"address"},{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"uint128","name":"amountToWithdraw","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amountOut","type":"uint128"}],"name":"LiquidWithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"refundWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferToRefundWallet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_MESSENGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GATEWAY_ROUTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTLEMENT_DISPATCHER_BRIDGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"binSponsor","outputs":[{"internalType":"enum BinSponsor","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnLD","type":"uint256"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"dataProvider","outputs":[{"internalType":"contract IEtherFiDataProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"destinationData","outputs":[{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getLiquidAssetWithdrawQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRefundWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_roleRegistry","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData[]","name":"_destDatas","type":"tuple[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"prepareRideBus","outputs":[{"internalType":"address","name":"stargate","type":"address"},{"internalType":"uint256","name":"valueToSend","type":"uint256"},{"internalType":"uint256","name":"minReturnFromStargate","type":"uint256"},{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"sendParam","type":"tuple"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"messagingFee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roleRegistry","outputs":[{"internalType":"contract IRoleRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"destEid","type":"uint32"},{"internalType":"address","name":"destRecipient","type":"address"},{"internalType":"address","name":"stargate","type":"address"},{"internalType":"bool","name":"useCanonicalBridge","type":"bool"},{"internalType":"uint64","name":"minGasLimit","type":"uint64"}],"internalType":"struct SettlementDispatcher.DestinationData[]","name":"destDatas","type":"tuple[]"}],"name":"setDestinationData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"boringQueue","type":"address"}],"name":"setLiquidAssetWithdrawQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFundsToRefundWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidToken","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"minReturn","type":"uint128"},{"internalType":"uint16","name":"discount","type":"uint16"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"name":"withdrawLiquidAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e060405230608052348015610013575f5ffd5b5060405161357638038061357683398101604081905261003291610127565b61003a610075565b81600181111561004c5761004c61016d565b60a08160018111156100605761006061016d565b9052506001600160a01b031660c05250610181565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff16156100c55760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146101245780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f5f60408385031215610138575f5ffd5b825160028110610146575f5ffd5b60208401519092506001600160a01b0381168114610162575f5ffd5b809150509250929050565b634e487b7160e01b5f52602160045260245ffd5b60805160a05160c0516133b66101c05f395f818161045d0152610cb401525f6105b201525f818161212801528181612151015261230201526133b65ff3fe60806040526004361061014a575f3560e01c80634f1ef286116100b35780638322fff21161006d5780638322fff2146103d45780638456cb59146103fb578063ad3cb1cc1461040f578063b334ed861461044c578063be3319781461047f578063cc614d68146105a1575f5ffd5b80634f1ef2861461030b57806352d1902d1461031e578063579f69d2146103405780635c975abb146103675780635d8d032414610395578063666fbf73146103b5575f5ffd5b80632a82afe2116101045780632a82afe21461024e5780632b03a50c14610262578063391c13f5146102815780633c6f7179146102a05780633f4ba83a146102d057806347437514146102e4575f5ffd5b806308c73259146101555780630bf5d35e146101865780630df6e6bf1461019b5780631c20fadd146101ba5780631eb2b17e146101d957806322d932f41461022f575f5ffd5b3661015157005b5f5ffd5b348015610160575f5ffd5b506101696105e1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461274e565b61060f565b005b3480156101a6575f5ffd5b506101996101b5366004612807565b6109a9565b3480156101c5575f5ffd5b506101996101d4366004612871565b610a4a565b3480156101e4575f5ffd5b506101696101f33660046128af565b6001600160a01b039081165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541690565b34801561023a575f5ffd5b506101996102493660046128ca565b610b6f565b348015610259575f5ffd5b50610169610cb1565b34801561026d575f5ffd5b5061019961027c3660046128f4565b610d37565b34801561028c575f5ffd5b5061019961029b36600461293f565b610f07565b3480156102ab575f5ffd5b506102bf6102ba3660046128ca565b6111c1565b60405161017d959493929190612a6d565b3480156102db575f5ffd5b506101996114c9565b3480156102ef575f5ffd5b50610169734c0926ff5252a435fd19e10ed15e5a249ba19d7981565b610199610319366004612b6a565b611530565b348015610329575f5ffd5b5061033261154b565b60405190815260200161017d565b34801561034b575f5ffd5b5061016973781e90f1c8fc4611c9b7497c3b47f99ef6969cbc81565b348015610372575f5ffd5b505f51602061336a5f395f51905f525460ff16604051901515815260200161017d565b3480156103a0575f5ffd5b506103325f51602061338a5f395f51905f5281565b3480156103c0575f5ffd5b506101996103cf366004612bf6565b611566565b3480156103df575f5ffd5b5061016973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610406575f5ffd5b50610199611682565b34801561041a575f5ffd5b5061043f604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161017d9190612c76565b348015610457575f5ffd5b506101697f000000000000000000000000000000000000000000000000000000000000000081565b34801561048a575f5ffd5b506105456104993660046128af565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f51602061334a5f395f51905f526001600160a01b039283165f9081526020918252604090819020815160a081018352815463ffffffff8116825264010000000090048616938101939093526001015493841690820152600160a01b830460ff1615156060820152600160a81b9092046001600160401b031660808301525090565b60408051825163ffffffff1681526020808401516001600160a01b03908116918301919091528383015116918101919091526060808301511515908201526080918201516001600160401b03169181019190915260a00161017d565b3480156105ac575f5ffd5b506105d47f000000000000000000000000000000000000000000000000000000000000000081565b60405161017d9190612c88565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500546001600160a01b031690565b6106176116e7565b5f51602061338a5f395f51905f5261062d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610678573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069c9190612cbb565b6106b8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841615806106cc575082155b156106ea57604051632a9ffab760e21b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610717575047610780565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015610759573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077d9190612cd6565b90505b838110156107a157604051631e9acf1760e31b815260040160405180910390fd5b5f5f51602061334a5f395f51905f526001600160a01b038781165f9081526020928352604090819020815160a081018352815463ffffffff8116825264010000000090048416948101949094526001015491821690830152600160a01b810460ff161580156060840152600160a81b9091046001600160401b0316608083015290915061084b576108458682602001518784608001516001600160401b0316611717565b506109a1565b5f5f5f5f5f61085a8b8b6111c1565b9450945094509450945082891115610885576040516395d2fadd60e01b815260040160405180910390fd5b834710156108a6576040516357630bad60e11b815260040160405180910390fd5b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146108de576108de6001600160a01b038c16868c611930565b5f856001600160a01b031663cbef2aa9868585306040518563ffffffff1660e01b815260040161091093929190612ced565b5f6040518083038185885af115801561092b573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526109539190810190612daa565b925050508b6001600160a01b03167fe80703b9d39d09d58bef629ae1125035c47d1da0342880315557c8ca9a2151cb8c83604051610992929190612ea4565b60405180910390a25050505050505b505050505050565b336109b26105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109ed573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a119190612eda565b6001600160a01b031614610a385760405163ab1899a760e01b815260040160405180910390fd5b610a44848484846119ed565b50505050565b610a52611eb2565b33610a5b6105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190612eda565b6001600160a01b031614610ae15760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b038216610b0857604051632a9ffab760e21b815260040160405180910390fd5b610b13838383611f1f565b9050816001600160a01b0316836001600160a01b03167fe9171aeff46c37ab955b8aff908e14ae9732bd06634fa131226cfe5c25bfd2fb83604051610b5a91815260200190565b60405180910390a3610b6a612094565b505050565b610b77611eb2565b5f51602061338a5f395f51905f52610b8d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610bd8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfc9190612cbb565b610c18576040516282b42960e81b815260040160405180910390fd5b5f610c21610cb1565b90506001600160a01b038116610c4a57604051630e8db52160e41b815260040160405180910390fd5b610c55848285611f1f565b604080516001600160a01b038088168252841660208201529081018290529093507fd8f7c78f293a52ff06285b09819d7625a13eb9080c991b7fac88cbf2b7239e899060600160405180910390a15050610cad612094565b5050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632a82afe26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d329190612eda565b905090565b33610d406105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9f9190612eda565b6001600160a01b031614610dc65760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b0382161580610de357506001600160a01b038116155b15610e0157604051632a9ffab760e21b815260040160405180910390fd5b806001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190612eda565b6001600160a01b0316826001600160a01b031614610e92576040516335c66c9960e01b815260040160405180910390fd5b6001600160a01b038281165f8181527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e01602052604080822080546001600160a01b0319169486169485179055517fdd001770b3f22fb8a32aee0705976d01646098f9c1254e8064d2ee09184490189190a35050565b5f51602061338a5f395f51905f52610f1d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610f68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8c9190612cbb565b610fa8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038781165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541680610fff57604051631d08fca760e11b815260040160405180910390fd5b60405163734d18e160e11b81526001600160a01b0388811660048301526001600160801b038816602483015261ffff861660448301525f919083169063e69a31c290606401602060405180830381865afa15801561105f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110839190612ef5565b9050856001600160801b0316816001600160801b031610156110b85760405163cea9e31d60e01b815260040160405180910390fd5b6110d56001600160a01b038a16836001600160801b038a16611930565b6040516335d9da3b60e11b81526001600160a01b0389811660048301526001600160801b038916602483015261ffff8716604483015262ffffff86166064830152831690636bb3b476906084016020604051808303815f875af115801561113e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111629190612cd6565b50604080516001600160801b03808a168252831660208201526001600160a01b03808b1692908c16917f51bf130244aab3e8452888df64bfe19c8c35a258af2516c5c1b791db7eae2f85910160405180910390a3505050505050505050565b5f5f5f6112066040518060e001604052805f63ffffffff1681526020015f81526020015f81526020015f81526020016060815260200160608152602001606081525090565b604080518082019091525f80825260208201525f5f51602061334a5f395f51905f526001600160a01b038981165f9081526020928352604090819020815160a081018352815463ffffffff811682526401000000009004841694810185905260019091015492831691810191909152600160a01b820460ff1615156060820152600160a81b9091046001600160401b0316608082015291506112bb57604051633df3d86f60e01b815260040160405180910390fd5b604081810151815160e081018352835163ffffffff1681526020808501516001600160a01b0316818301528184018b9052606082018b905283515f808252818301865260808401919091528451908152808201855260a083015283516001808252818601909552929950909260c084019291820181803683375050509052604051630d35b41560e01b81529093505f906001600160a01b03881690630d35b4159061136a908790600401612f10565b5f60405180830381865afa158015611384573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113ab9190810190612f22565b602081018051606089015251604051633b6f743b60e01b81529098509093506001600160a01b038a169250633b6f743b91506113ed9087905f9060040161306b565b6040805180830381865afa158015611407573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142b919061308e565b9250825f015195505f6001600160a01b0316876001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611479573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149d9190612eda565b6001600160a01b0316036114bd5760408401516114ba90876130a8565b95505b50509295509295909350565b6114d16105e1565b6040516317bf301f60e21b81523360048201526001600160a01b039190911690635efcc07c906024015f6040518083038186803b158015611510575f5ffd5b505afa158015611522573d5f5f3e3d5ffd5b5050505061152e6120be565b565b61153861211d565b611541826121c1565b610cad8282612236565b5f6115546122f7565b505f51602061332a5f395f51905f5290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156115aa5750825b90505f826001600160401b031660011480156115c55750303b155b9050811580156115d3575080155b156115f15760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561161b57845460ff60401b1916600160401b1785555b6116248a612340565b611630898989896119ed565b831561167657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b61168a6105e1565b604051632fd0067b60e21b81523360048201526001600160a01b03919091169063bf4019ec906024015f6040518083038186803b1580156116c9575f5ffd5b505afa1580156116db573d5f5f3e3d5ffd5b5050505061152e612392565b5f51602061336a5f395f51905f525460ff161561152e5760405163d93c066560e01b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016117c85760405163b2267a7b60e01b81526001600160a01b038516600482015260248101849052608060448201525f60848201526064810183905273781e90f1c8fc4611c9b7497c3b47f99ef6969cbc9063b2267a7b90859060a4015f604051808303818588803b1580156117ac575f5ffd5b505af11580156117be573d5f5f3e3d5ffd5b50505050506118da565b6040516343c6674160e01b81526001600160a01b03861660048201525f90734c0926ff5252a435fd19e10ed15e5a249ba19d79906343c6674190602401602060405180830381865afa158015611820573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118449190612eda565b905061185a6001600160a01b0387168286611930565b60405163a93a4af960e01b81526001600160a01b038088166004830152861660248201526044810185905260648101849052734c0926ff5252a435fd19e10ed15e5a249ba19d799063a93a4af9906084015f604051808303815f87803b1580156118c2575f5ffd5b505af11580156118d4573d5f5f3e3d5ffd5b50505050505b836001600160a01b0316856001600160a01b03167f89fb53d0c0f0f36db1043a54a7bb546fd820379fef6a90aec7293c3cf7ca13c98560405161191f91815260200190565b60405180910390a350909392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261198184826123da565b610a44576040516001600160a01b0384811660248301525f60448301526119e391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612425565b610a448482612425565b82818114611a0e5760405163512509d360e11b815260040160405180910390fd5b5f51602061334a5f395f51905f525f5b82811015611e6c57848482818110611a3857611a386130c7565b905060a002016060016020810190611a5091906130db565b15611b5a575f878783818110611a6857611a686130c7565b9050602002016020810190611a7d91906128af565b6001600160a01b03161480611ac357505f858583818110611aa057611aa06130c7565b905060a002016020016020810190611ab891906128af565b6001600160a01b0316145b80611b0057505f858583818110611adc57611adc6130c7565b905060a002016040016020810190611af491906128af565b6001600160a01b031614155b80611b375750848482818110611b1857611b186130c7565b611b2e92602060a0909202019081019150613107565b63ffffffff1615155b15611b5557604051632a9ffab760e21b815260040160405180910390fd5b611dfd565b5f878783818110611b6d57611b6d6130c7565b9050602002016020810190611b8291906128af565b6001600160a01b03161480611bc857505f858583818110611ba557611ba56130c7565b905060a002016020016020810190611bbd91906128af565b6001600160a01b0316145b80611c0457505f858583818110611be157611be16130c7565b905060a002016040016020810190611bf991906128af565b6001600160a01b0316145b15611c2257604051632a9ffab760e21b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee878783818110611c4957611c496130c7565b9050602002016020810190611c5e91906128af565b6001600160a01b031603611d1d575f858583818110611c7f57611c7f6130c7565b905060a002016040016020810190611c9791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cd2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cf69190612eda565b6001600160a01b031614611b555760405163b0fcd49560e01b815260040160405180910390fd5b868682818110611d2f57611d2f6130c7565b9050602002016020810190611d4491906128af565b6001600160a01b0316858583818110611d5f57611d5f6130c7565b905060a002016040016020810190611d7791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd69190612eda565b6001600160a01b031614611dfd5760405163b0fcd49560e01b815260040160405180910390fd5b848482818110611e0f57611e0f6130c7565b905060a00201825f015f898985818110611e2b57611e2b6130c7565b9050602002016020810190611e4091906128af565b6001600160a01b0316815260208101919091526040015f20611e62828261312e565b5050600101611a1e565b507f93c0d4c04381b5de5cd6f8911c1885ffa461192db849a9e32edaea31816d5a9c86868686604051611ea29493929190613217565b60405180910390a1505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15611ef257604051633ee5aeb560e01b815260040160405180910390fd5b61152e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90612491565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03851601611fe657815f03611f50574791505b815f03611f705760405163c740c32d60e01b815260040160405180910390fd5b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611fb9576040519150601f19603f3d011682016040523d82523d5f602084013e611fbe565b606091505b5050905080611fe057604051632607a81f60e11b815260040160405180910390fd5b5061208a565b815f03612056576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561202f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120539190612cd6565b91505b815f036120765760405163c740c32d60e01b815260040160405180910390fd5b61208a6001600160a01b0385168484612498565b50805b9392505050565b61152e5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00611f19565b6120c66124c9565b5f51602061336a5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806121a357507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166121975f51602061332a5f395f51905f52546001600160a01b031690565b6001600160a01b031614155b1561152e5760405163703e46dd60e11b815260040160405180910390fd5b5f7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f5008054604051634766d28760e01b81523360048201529192506001600160a01b031690634766d287906024015f6040518083038186803b158015612224575f5ffd5b505afa1580156109a1573d5f5f3e3d5ffd5b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612290575060408051601f3d908101601f1916820190925261228d91810190612cd6565b60015b6122bd57604051634c9c8ce360e01b81526001600160a01b03831660048201526024015b60405180910390fd5b5f51602061332a5f395f51905f5281146122ed57604051632a87526960e21b8152600481018290526024016122b4565b610b6a83836124f8565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461152e5760405163703e46dd60e11b815260040160405180910390fd5b61234861254d565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f50080546001600160a01b0319166001600160a01b03831617815561238a612596565b610cad61259e565b61239a6116e7565b5f51602061336a5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336120ff565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156124195750811561240b5780600114612419565b5f866001600160a01b03163b115b93505050505b92915050565b5f5f60205f8451602086015f885af180612444576040513d5f823e3d81fd5b50505f513d9150811561245b578060011415612468565b6001600160a01b0384163b155b15610a4457604051635274afe760e01b81526001600160a01b03851660048201526024016122b4565b80825d5050565b6040516001600160a01b03838116602483015260448201839052610b6a91859182169063a9059cbb906064016119b1565b5f51602061336a5f395f51905f525460ff1661152e57604051638dfc202b60e01b815260040160405180910390fd5b612501826125be565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561254557610b6a8282612621565b610cad612693565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661152e57604051631afcd79f60e31b815260040160405180910390fd5b61152e61254d565b6125a661254d565b5f51602061336a5f395f51905f52805460ff19169055565b806001600160a01b03163b5f036125f357604051634c9c8ce360e01b81526001600160a01b03821660048201526024016122b4565b5f51602061332a5f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161263d9190613313565b5f60405180830381855af49150503d805f8114612675576040519150601f19603f3d011682016040523d82523d5f602084013e61267a565b606091505b509150915061268a8583836126b2565b95945050505050565b341561152e5760405163b398979f60e01b815260040160405180910390fd5b6060826126c7576126c28261270e565b61208d565b81511580156126de57506001600160a01b0384163b155b1561270757604051639996b31560e01b81526001600160a01b03851660048201526024016122b4565b508061208d565b80511561271e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b6001600160a01b0381168114612737575f5ffd5b5f5f5f60608486031215612760575f5ffd5b833561276b8161273a565b95602085013595506040909401359392505050565b5f5f83601f840112612790575f5ffd5b5081356001600160401b038111156127a6575f5ffd5b6020830191508360208260051b85010111156127c0575f5ffd5b9250929050565b5f5f83601f8401126127d7575f5ffd5b5081356001600160401b038111156127ed575f5ffd5b60208301915083602060a0830285010111156127c0575f5ffd5b5f5f5f5f6040858703121561281a575f5ffd5b84356001600160401b0381111561282f575f5ffd5b61283b87828801612780565b90955093505060208501356001600160401b03811115612859575f5ffd5b612865878288016127c7565b95989497509550505050565b5f5f5f60608486031215612883575f5ffd5b833561288e8161273a565b9250602084013561289e8161273a565b929592945050506040919091013590565b5f602082840312156128bf575f5ffd5b813561208d8161273a565b5f5f604083850312156128db575f5ffd5b82356128e68161273a565b946020939093013593505050565b5f5f60408385031215612905575f5ffd5b82356129108161273a565b915060208301356129208161273a565b809150509250929050565b6001600160801b0381168114612737575f5ffd5b5f5f5f5f5f5f60c08789031215612954575f5ffd5b863561295f8161273a565b9550602087013561296f8161273a565b9450604087013561297f8161292b565b9350606087013561298f8161292b565b9250608087013561ffff811681146129a5575f5ffd5b915060a087013562ffffff811681146129bc575f5ffd5b809150509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612a3a60e08501826129ca565b905060a083015184820360a0860152612a5382826129ca565b91505060c083015184820360c086015261268a82826129ca565b60018060a01b038616815284602082015283604082015260c060608201525f612a9960c08301856129f8565b83516080840152602084015160a084015290509695505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715612aec57612aec612ab6565b60405290565b604051606081016001600160401b0381118282101715612aec57612aec612ab6565b604051601f8201601f191681016001600160401b0381118282101715612b3c57612b3c612ab6565b604052919050565b5f6001600160401b03821115612b5c57612b5c612ab6565b50601f01601f191660200190565b5f5f60408385031215612b7b575f5ffd5b8235612b868161273a565b915060208301356001600160401b03811115612ba0575f5ffd5b8301601f81018513612bb0575f5ffd5b8035612bc3612bbe82612b44565b612b14565b818152866020838501011115612bd7575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60608688031215612c0a575f5ffd5b8535612c158161273a565b945060208601356001600160401b03811115612c2f575f5ffd5b612c3b88828901612780565b90955093505060408601356001600160401b03811115612c59575f5ffd5b612c65888289016127c7565b969995985093965092949392505050565b602081525f61208d60208301846129ca565b6020810160028310612ca857634e487b7160e01b5f52602160045260245ffd5b91905290565b8015158114612737575f5ffd5b5f60208284031215612ccb575f5ffd5b815161208d81612cae565b5f60208284031215612ce6575f5ffd5b5051919050565b608081525f612cff60808301866129f8565b9050612d18602083018580518252602090810151910152565b6001600160a01b03929092166060919091015292915050565b6001600160401b0381168114612737575f5ffd5b5f60408284031215612d55575f5ffd5b612d5d612aca565b825181526020928301519281019290925250919050565b5f612d81612bbe84612b44565b9050828152838383011115612d94575f5ffd5b8282602083015e5f602084830101529392505050565b5f5f5f83850360e0811215612dbd575f5ffd5b6080811215612dca575f5ffd5b50612dd3612af2565b845181526020850151612de581612d31565b6020820152612df78660408701612d45565b60408201529250612e0b8560808601612d45565b915060c08401516001600160401b03811115612e25575f5ffd5b840160408187031215612e36575f5ffd5b612e3e612aca565b815168ffffffffffffffffff81168114612e56575f5ffd5b815260208201516001600160401b03811115612e70575f5ffd5b80830192505086601f830112612e84575f5ffd5b612e9387835160208501612d74565b602082015280925050509250925092565b8281526040602082015268ffffffffffffffffff82511660408201525f60208301516040606084015261268a60808401826129ca565b5f60208284031215612eea575f5ffd5b815161208d8161273a565b5f60208284031215612f05575f5ffd5b815161208d8161292b565b602081525f61208d60208301846129f8565b5f5f5f60a08486031215612f34575f5ffd5b612f3e8585612d45565b925060408401516001600160401b03811115612f58575f5ffd5b8401601f81018613612f68575f5ffd5b80516001600160401b03811115612f8157612f81612ab6565b8060051b612f9160208201612b14565b91825260208184018101929081019089841115612fac575f5ffd5b6020850192505b8383101561304d5782516001600160401b03811115612fd0575f5ffd5b85016040818c03601f19011215612fe5575f5ffd5b612fed612aca565b6020820151815260408201516001600160401b0381111561300c575f5ffd5b6020818401019250508b601f830112613023575f5ffd5b6130328c835160208501612d74565b60208201528084525050602082019150602083019250612fb3565b80965050505050506130628560608601612d45565b90509250925092565b604081525f61307d60408301856129f8565b905082151560208301529392505050565b5f6040828403121561309e575f5ffd5b61208d8383612d45565b8082018082111561241f57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156130eb575f5ffd5b813561208d81612cae565b63ffffffff81168114612737575f5ffd5b5f60208284031215613117575f5ffd5b813561208d816130f6565b5f813561241f81612d31565b8135613139816130f6565b63ffffffff8116905081548163ffffffff198216178355602084013561315e8161273a565b6001600160c01b03199190911690911760209190911b640100000000600160c01b0316178155600181015f60408401356131978161273a565b82546001600160a01b0319166001600160a01b03909116178255505f60608401356131c181612cae565b825460ff60a01b191681151560a01b60ff60a01b16178355905050610b6a6131eb60808501613122565b82805467ffffffffffffffff60a81b191660a89290921b67ffffffffffffffff60a81b16919091179055565b604080825281018490525f8560608301825b8781101561325957823561323c8161273a565b6001600160a01b0316825260209283019290910190600101613229565b50838103602080860191909152858252019050845f5b85811015613306578135613282816130f6565b63ffffffff16835260208201356132988161273a565b6001600160a01b0316602084015260408201356132b48161273a565b6001600160a01b0316604084015260608201356132d081612cae565b1515606084015260808201356132e581612d31565b6001600160401b0316608084015260a092830192919091019060010161326f565b5090979650505050505050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300349c08c4c7eff6f18e11e7787aea968e8ed1e26c7307dfcd722a9d753313069da164736f6c634300081c000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778

Deployed Bytecode

0x60806040526004361061014a575f3560e01c80634f1ef286116100b35780638322fff21161006d5780638322fff2146103d45780638456cb59146103fb578063ad3cb1cc1461040f578063b334ed861461044c578063be3319781461047f578063cc614d68146105a1575f5ffd5b80634f1ef2861461030b57806352d1902d1461031e578063579f69d2146103405780635c975abb146103675780635d8d032414610395578063666fbf73146103b5575f5ffd5b80632a82afe2116101045780632a82afe21461024e5780632b03a50c14610262578063391c13f5146102815780633c6f7179146102a05780633f4ba83a146102d057806347437514146102e4575f5ffd5b806308c73259146101555780630bf5d35e146101865780630df6e6bf1461019b5780631c20fadd146101ba5780631eb2b17e146101d957806322d932f41461022f575f5ffd5b3661015157005b5f5ffd5b348015610160575f5ffd5b506101696105e1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461274e565b61060f565b005b3480156101a6575f5ffd5b506101996101b5366004612807565b6109a9565b3480156101c5575f5ffd5b506101996101d4366004612871565b610a4a565b3480156101e4575f5ffd5b506101696101f33660046128af565b6001600160a01b039081165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541690565b34801561023a575f5ffd5b506101996102493660046128ca565b610b6f565b348015610259575f5ffd5b50610169610cb1565b34801561026d575f5ffd5b5061019961027c3660046128f4565b610d37565b34801561028c575f5ffd5b5061019961029b36600461293f565b610f07565b3480156102ab575f5ffd5b506102bf6102ba3660046128ca565b6111c1565b60405161017d959493929190612a6d565b3480156102db575f5ffd5b506101996114c9565b3480156102ef575f5ffd5b50610169734c0926ff5252a435fd19e10ed15e5a249ba19d7981565b610199610319366004612b6a565b611530565b348015610329575f5ffd5b5061033261154b565b60405190815260200161017d565b34801561034b575f5ffd5b5061016973781e90f1c8fc4611c9b7497c3b47f99ef6969cbc81565b348015610372575f5ffd5b505f51602061336a5f395f51905f525460ff16604051901515815260200161017d565b3480156103a0575f5ffd5b506103325f51602061338a5f395f51905f5281565b3480156103c0575f5ffd5b506101996103cf366004612bf6565b611566565b3480156103df575f5ffd5b5061016973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b348015610406575f5ffd5b50610199611682565b34801561041a575f5ffd5b5061043f604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161017d9190612c76565b348015610457575f5ffd5b506101697f000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a77881565b34801561048a575f5ffd5b506105456104993660046128af565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f51602061334a5f395f51905f526001600160a01b039283165f9081526020918252604090819020815160a081018352815463ffffffff8116825264010000000090048616938101939093526001015493841690820152600160a01b830460ff1615156060820152600160a81b9092046001600160401b031660808301525090565b60408051825163ffffffff1681526020808401516001600160a01b03908116918301919091528383015116918101919091526060808301511515908201526080918201516001600160401b03169181019190915260a00161017d565b3480156105ac575f5ffd5b506105d47f000000000000000000000000000000000000000000000000000000000000000181565b60405161017d9190612c88565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f500546001600160a01b031690565b6106176116e7565b5f51602061338a5f395f51905f5261062d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610678573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069c9190612cbb565b6106b8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841615806106cc575082155b156106ea57604051632a9ffab760e21b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610717575047610780565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015610759573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077d9190612cd6565b90505b838110156107a157604051631e9acf1760e31b815260040160405180910390fd5b5f5f51602061334a5f395f51905f526001600160a01b038781165f9081526020928352604090819020815160a081018352815463ffffffff8116825264010000000090048416948101949094526001015491821690830152600160a01b810460ff161580156060840152600160a81b9091046001600160401b0316608083015290915061084b576108458682602001518784608001516001600160401b0316611717565b506109a1565b5f5f5f5f5f61085a8b8b6111c1565b9450945094509450945082891115610885576040516395d2fadd60e01b815260040160405180910390fd5b834710156108a6576040516357630bad60e11b815260040160405180910390fd5b6001600160a01b038b1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146108de576108de6001600160a01b038c16868c611930565b5f856001600160a01b031663cbef2aa9868585306040518563ffffffff1660e01b815260040161091093929190612ced565b5f6040518083038185885af115801561092b573d5f5f3e3d5ffd5b50505050506040513d5f823e601f3d908101601f191682016040526109539190810190612daa565b925050508b6001600160a01b03167fe80703b9d39d09d58bef629ae1125035c47d1da0342880315557c8ca9a2151cb8c83604051610992929190612ea4565b60405180910390a25050505050505b505050505050565b336109b26105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109ed573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a119190612eda565b6001600160a01b031614610a385760405163ab1899a760e01b815260040160405180910390fd5b610a44848484846119ed565b50505050565b610a52611eb2565b33610a5b6105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a96573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aba9190612eda565b6001600160a01b031614610ae15760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b038216610b0857604051632a9ffab760e21b815260040160405180910390fd5b610b13838383611f1f565b9050816001600160a01b0316836001600160a01b03167fe9171aeff46c37ab955b8aff908e14ae9732bd06634fa131226cfe5c25bfd2fb83604051610b5a91815260200190565b60405180910390a3610b6a612094565b505050565b610b77611eb2565b5f51602061338a5f395f51905f52610b8d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610bd8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfc9190612cbb565b610c18576040516282b42960e81b815260040160405180910390fd5b5f610c21610cb1565b90506001600160a01b038116610c4a57604051630e8db52160e41b815260040160405180910390fd5b610c55848285611f1f565b604080516001600160a01b038088168252841660208201529081018290529093507fd8f7c78f293a52ff06285b09819d7625a13eb9080c991b7fac88cbf2b7239e899060600160405180910390a15050610cad612094565b5050565b5f7f000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a7786001600160a01b0316632a82afe26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d329190612eda565b905090565b33610d406105e1565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9f9190612eda565b6001600160a01b031614610dc65760405163ab1899a760e01b815260040160405180910390fd5b6001600160a01b0382161580610de357506001600160a01b038116155b15610e0157604051632a9ffab760e21b815260040160405180910390fd5b806001600160a01b031663f3b977846040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e619190612eda565b6001600160a01b0316826001600160a01b031614610e92576040516335c66c9960e01b815260040160405180910390fd5b6001600160a01b038281165f8181527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e01602052604080822080546001600160a01b0319169486169485179055517fdd001770b3f22fb8a32aee0705976d01646098f9c1254e8064d2ee09184490189190a35050565b5f51602061338a5f395f51905f52610f1d6105e1565b604051632474521560e21b8152600481018390523360248201526001600160a01b0391909116906391d1485490604401602060405180830381865afa158015610f68573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8c9190612cbb565b610fa8576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038781165f9081527f78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e0160205260409020541680610fff57604051631d08fca760e11b815260040160405180910390fd5b60405163734d18e160e11b81526001600160a01b0388811660048301526001600160801b038816602483015261ffff861660448301525f919083169063e69a31c290606401602060405180830381865afa15801561105f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110839190612ef5565b9050856001600160801b0316816001600160801b031610156110b85760405163cea9e31d60e01b815260040160405180910390fd5b6110d56001600160a01b038a16836001600160801b038a16611930565b6040516335d9da3b60e11b81526001600160a01b0389811660048301526001600160801b038916602483015261ffff8716604483015262ffffff86166064830152831690636bb3b476906084016020604051808303815f875af115801561113e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111629190612cd6565b50604080516001600160801b03808a168252831660208201526001600160a01b03808b1692908c16917f51bf130244aab3e8452888df64bfe19c8c35a258af2516c5c1b791db7eae2f85910160405180910390a3505050505050505050565b5f5f5f6112066040518060e001604052805f63ffffffff1681526020015f81526020015f81526020015f81526020016060815260200160608152602001606081525090565b604080518082019091525f80825260208201525f5f51602061334a5f395f51905f526001600160a01b038981165f9081526020928352604090819020815160a081018352815463ffffffff811682526401000000009004841694810185905260019091015492831691810191909152600160a01b820460ff1615156060820152600160a81b9091046001600160401b0316608082015291506112bb57604051633df3d86f60e01b815260040160405180910390fd5b604081810151815160e081018352835163ffffffff1681526020808501516001600160a01b0316818301528184018b9052606082018b905283515f808252818301865260808401919091528451908152808201855260a083015283516001808252818601909552929950909260c084019291820181803683375050509052604051630d35b41560e01b81529093505f906001600160a01b03881690630d35b4159061136a908790600401612f10565b5f60405180830381865afa158015611384573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113ab9190810190612f22565b602081018051606089015251604051633b6f743b60e01b81529098509093506001600160a01b038a169250633b6f743b91506113ed9087905f9060040161306b565b6040805180830381865afa158015611407573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142b919061308e565b9250825f015195505f6001600160a01b0316876001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611479573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149d9190612eda565b6001600160a01b0316036114bd5760408401516114ba90876130a8565b95505b50509295509295909350565b6114d16105e1565b6040516317bf301f60e21b81523360048201526001600160a01b039190911690635efcc07c906024015f6040518083038186803b158015611510575f5ffd5b505afa158015611522573d5f5f3e3d5ffd5b5050505061152e6120be565b565b61153861211d565b611541826121c1565b610cad8282612236565b5f6115546122f7565b505f51602061332a5f395f51905f5290565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156115aa5750825b90505f826001600160401b031660011480156115c55750303b155b9050811580156115d3575080155b156115f15760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561161b57845460ff60401b1916600160401b1785555b6116248a612340565b611630898989896119ed565b831561167657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b61168a6105e1565b604051632fd0067b60e21b81523360048201526001600160a01b03919091169063bf4019ec906024015f6040518083038186803b1580156116c9575f5ffd5b505afa1580156116db573d5f5f3e3d5ffd5b5050505061152e612392565b5f51602061336a5f395f51905f525460ff161561152e5760405163d93c066560e01b815260040160405180910390fd5b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016117c85760405163b2267a7b60e01b81526001600160a01b038516600482015260248101849052608060448201525f60848201526064810183905273781e90f1c8fc4611c9b7497c3b47f99ef6969cbc9063b2267a7b90859060a4015f604051808303818588803b1580156117ac575f5ffd5b505af11580156117be573d5f5f3e3d5ffd5b50505050506118da565b6040516343c6674160e01b81526001600160a01b03861660048201525f90734c0926ff5252a435fd19e10ed15e5a249ba19d79906343c6674190602401602060405180830381865afa158015611820573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118449190612eda565b905061185a6001600160a01b0387168286611930565b60405163a93a4af960e01b81526001600160a01b038088166004830152861660248201526044810185905260648101849052734c0926ff5252a435fd19e10ed15e5a249ba19d799063a93a4af9906084015f604051808303815f87803b1580156118c2575f5ffd5b505af11580156118d4573d5f5f3e3d5ffd5b50505050505b836001600160a01b0316856001600160a01b03167f89fb53d0c0f0f36db1043a54a7bb546fd820379fef6a90aec7293c3cf7ca13c98560405161191f91815260200190565b60405180910390a350909392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261198184826123da565b610a44576040516001600160a01b0384811660248301525f60448301526119e391869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612425565b610a448482612425565b82818114611a0e5760405163512509d360e11b815260040160405180910390fd5b5f51602061334a5f395f51905f525f5b82811015611e6c57848482818110611a3857611a386130c7565b905060a002016060016020810190611a5091906130db565b15611b5a575f878783818110611a6857611a686130c7565b9050602002016020810190611a7d91906128af565b6001600160a01b03161480611ac357505f858583818110611aa057611aa06130c7565b905060a002016020016020810190611ab891906128af565b6001600160a01b0316145b80611b0057505f858583818110611adc57611adc6130c7565b905060a002016040016020810190611af491906128af565b6001600160a01b031614155b80611b375750848482818110611b1857611b186130c7565b611b2e92602060a0909202019081019150613107565b63ffffffff1615155b15611b5557604051632a9ffab760e21b815260040160405180910390fd5b611dfd565b5f878783818110611b6d57611b6d6130c7565b9050602002016020810190611b8291906128af565b6001600160a01b03161480611bc857505f858583818110611ba557611ba56130c7565b905060a002016020016020810190611bbd91906128af565b6001600160a01b0316145b80611c0457505f858583818110611be157611be16130c7565b905060a002016040016020810190611bf991906128af565b6001600160a01b0316145b15611c2257604051632a9ffab760e21b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee878783818110611c4957611c496130c7565b9050602002016020810190611c5e91906128af565b6001600160a01b031603611d1d575f858583818110611c7f57611c7f6130c7565b905060a002016040016020810190611c9791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cd2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cf69190612eda565b6001600160a01b031614611b555760405163b0fcd49560e01b815260040160405180910390fd5b868682818110611d2f57611d2f6130c7565b9050602002016020810190611d4491906128af565b6001600160a01b0316858583818110611d5f57611d5f6130c7565b905060a002016040016020810190611d7791906128af565b6001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd69190612eda565b6001600160a01b031614611dfd5760405163b0fcd49560e01b815260040160405180910390fd5b848482818110611e0f57611e0f6130c7565b905060a00201825f015f898985818110611e2b57611e2b6130c7565b9050602002016020810190611e4091906128af565b6001600160a01b0316815260208101919091526040015f20611e62828261312e565b5050600101611a1e565b507f93c0d4c04381b5de5cd6f8911c1885ffa461192db849a9e32edaea31816d5a9c86868686604051611ea29493929190613217565b60405180910390a1505050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15611ef257604051633ee5aeb560e01b815260040160405180910390fd5b61152e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90612491565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03851601611fe657815f03611f50574791505b815f03611f705760405163c740c32d60e01b815260040160405180910390fd5b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611fb9576040519150601f19603f3d011682016040523d82523d5f602084013e611fbe565b606091505b5050905080611fe057604051632607a81f60e11b815260040160405180910390fd5b5061208a565b815f03612056576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa15801561202f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120539190612cd6565b91505b815f036120765760405163c740c32d60e01b815260040160405180910390fd5b61208a6001600160a01b0385168484612498565b50805b9392505050565b61152e5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00611f19565b6120c66124c9565b5f51602061336a5f395f51905f52805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b306001600160a01b037f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee921614806121a357507f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee926001600160a01b03166121975f51602061332a5f395f51905f52546001600160a01b031690565b6001600160a01b031614155b1561152e5760405163703e46dd60e11b815260040160405180910390fd5b5f7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f5008054604051634766d28760e01b81523360048201529192506001600160a01b031690634766d287906024015f6040518083038186803b158015612224575f5ffd5b505afa1580156109a1573d5f5f3e3d5ffd5b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612290575060408051601f3d908101601f1916820190925261228d91810190612cd6565b60015b6122bd57604051634c9c8ce360e01b81526001600160a01b03831660048201526024015b60405180910390fd5b5f51602061332a5f395f51905f5281146122ed57604051632a87526960e21b8152600481018290526024016122b4565b610b6a83836124f8565b306001600160a01b037f00000000000000000000000049859e57be078a0b9a396cbbf1b5197d13caee92161461152e5760405163703e46dd60e11b815260040160405180910390fd5b61234861254d565b7fa5586bb7fe6c4d1a576fc53fefe6d5915940638d338769f6905020734977f50080546001600160a01b0319166001600160a01b03831617815561238a612596565b610cad61259e565b61239a6116e7565b5f51602061336a5f395f51905f52805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258336120ff565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156124195750811561240b5780600114612419565b5f866001600160a01b03163b115b93505050505b92915050565b5f5f60205f8451602086015f885af180612444576040513d5f823e3d81fd5b50505f513d9150811561245b578060011415612468565b6001600160a01b0384163b155b15610a4457604051635274afe760e01b81526001600160a01b03851660048201526024016122b4565b80825d5050565b6040516001600160a01b03838116602483015260448201839052610b6a91859182169063a9059cbb906064016119b1565b5f51602061336a5f395f51905f525460ff1661152e57604051638dfc202b60e01b815260040160405180910390fd5b612501826125be565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561254557610b6a8282612621565b610cad612693565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661152e57604051631afcd79f60e31b815260040160405180910390fd5b61152e61254d565b6125a661254d565b5f51602061336a5f395f51905f52805460ff19169055565b806001600160a01b03163b5f036125f357604051634c9c8ce360e01b81526001600160a01b03821660048201526024016122b4565b5f51602061332a5f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b03168460405161263d9190613313565b5f60405180830381855af49150503d805f8114612675576040519150601f19603f3d011682016040523d82523d5f602084013e61267a565b606091505b509150915061268a8583836126b2565b95945050505050565b341561152e5760405163b398979f60e01b815260040160405180910390fd5b6060826126c7576126c28261270e565b61208d565b81511580156126de57506001600160a01b0384163b155b1561270757604051639996b31560e01b81526001600160a01b03851660048201526024016122b4565b508061208d565b80511561271e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b50565b6001600160a01b0381168114612737575f5ffd5b5f5f5f60608486031215612760575f5ffd5b833561276b8161273a565b95602085013595506040909401359392505050565b5f5f83601f840112612790575f5ffd5b5081356001600160401b038111156127a6575f5ffd5b6020830191508360208260051b85010111156127c0575f5ffd5b9250929050565b5f5f83601f8401126127d7575f5ffd5b5081356001600160401b038111156127ed575f5ffd5b60208301915083602060a0830285010111156127c0575f5ffd5b5f5f5f5f6040858703121561281a575f5ffd5b84356001600160401b0381111561282f575f5ffd5b61283b87828801612780565b90955093505060208501356001600160401b03811115612859575f5ffd5b612865878288016127c7565b95989497509550505050565b5f5f5f60608486031215612883575f5ffd5b833561288e8161273a565b9250602084013561289e8161273a565b929592945050506040919091013590565b5f602082840312156128bf575f5ffd5b813561208d8161273a565b5f5f604083850312156128db575f5ffd5b82356128e68161273a565b946020939093013593505050565b5f5f60408385031215612905575f5ffd5b82356129108161273a565b915060208301356129208161273a565b809150509250929050565b6001600160801b0381168114612737575f5ffd5b5f5f5f5f5f5f60c08789031215612954575f5ffd5b863561295f8161273a565b9550602087013561296f8161273a565b9450604087013561297f8161292b565b9350606087013561298f8161292b565b9250608087013561ffff811681146129a5575f5ffd5b915060a087013562ffffff811681146129bc575f5ffd5b809150509295509295509295565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612a3a60e08501826129ca565b905060a083015184820360a0860152612a5382826129ca565b91505060c083015184820360c086015261268a82826129ca565b60018060a01b038616815284602082015283604082015260c060608201525f612a9960c08301856129f8565b83516080840152602084015160a084015290509695505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715612aec57612aec612ab6565b60405290565b604051606081016001600160401b0381118282101715612aec57612aec612ab6565b604051601f8201601f191681016001600160401b0381118282101715612b3c57612b3c612ab6565b604052919050565b5f6001600160401b03821115612b5c57612b5c612ab6565b50601f01601f191660200190565b5f5f60408385031215612b7b575f5ffd5b8235612b868161273a565b915060208301356001600160401b03811115612ba0575f5ffd5b8301601f81018513612bb0575f5ffd5b8035612bc3612bbe82612b44565b612b14565b818152866020838501011115612bd7575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f5f5f60608688031215612c0a575f5ffd5b8535612c158161273a565b945060208601356001600160401b03811115612c2f575f5ffd5b612c3b88828901612780565b90955093505060408601356001600160401b03811115612c59575f5ffd5b612c65888289016127c7565b969995985093965092949392505050565b602081525f61208d60208301846129ca565b6020810160028310612ca857634e487b7160e01b5f52602160045260245ffd5b91905290565b8015158114612737575f5ffd5b5f60208284031215612ccb575f5ffd5b815161208d81612cae565b5f60208284031215612ce6575f5ffd5b5051919050565b608081525f612cff60808301866129f8565b9050612d18602083018580518252602090810151910152565b6001600160a01b03929092166060919091015292915050565b6001600160401b0381168114612737575f5ffd5b5f60408284031215612d55575f5ffd5b612d5d612aca565b825181526020928301519281019290925250919050565b5f612d81612bbe84612b44565b9050828152838383011115612d94575f5ffd5b8282602083015e5f602084830101529392505050565b5f5f5f83850360e0811215612dbd575f5ffd5b6080811215612dca575f5ffd5b50612dd3612af2565b845181526020850151612de581612d31565b6020820152612df78660408701612d45565b60408201529250612e0b8560808601612d45565b915060c08401516001600160401b03811115612e25575f5ffd5b840160408187031215612e36575f5ffd5b612e3e612aca565b815168ffffffffffffffffff81168114612e56575f5ffd5b815260208201516001600160401b03811115612e70575f5ffd5b80830192505086601f830112612e84575f5ffd5b612e9387835160208501612d74565b602082015280925050509250925092565b8281526040602082015268ffffffffffffffffff82511660408201525f60208301516040606084015261268a60808401826129ca565b5f60208284031215612eea575f5ffd5b815161208d8161273a565b5f60208284031215612f05575f5ffd5b815161208d8161292b565b602081525f61208d60208301846129f8565b5f5f5f60a08486031215612f34575f5ffd5b612f3e8585612d45565b925060408401516001600160401b03811115612f58575f5ffd5b8401601f81018613612f68575f5ffd5b80516001600160401b03811115612f8157612f81612ab6565b8060051b612f9160208201612b14565b91825260208184018101929081019089841115612fac575f5ffd5b6020850192505b8383101561304d5782516001600160401b03811115612fd0575f5ffd5b85016040818c03601f19011215612fe5575f5ffd5b612fed612aca565b6020820151815260408201516001600160401b0381111561300c575f5ffd5b6020818401019250508b601f830112613023575f5ffd5b6130328c835160208501612d74565b60208201528084525050602082019150602083019250612fb3565b80965050505050506130628560608601612d45565b90509250925092565b604081525f61307d60408301856129f8565b905082151560208301529392505050565b5f6040828403121561309e575f5ffd5b61208d8383612d45565b8082018082111561241f57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156130eb575f5ffd5b813561208d81612cae565b63ffffffff81168114612737575f5ffd5b5f60208284031215613117575f5ffd5b813561208d816130f6565b5f813561241f81612d31565b8135613139816130f6565b63ffffffff8116905081548163ffffffff198216178355602084013561315e8161273a565b6001600160c01b03199190911690911760209190911b640100000000600160c01b0316178155600181015f60408401356131978161273a565b82546001600160a01b0319166001600160a01b03909116178255505f60608401356131c181612cae565b825460ff60a01b191681151560a01b60ff60a01b16178355905050610b6a6131eb60808501613122565b82805467ffffffffffffffff60a81b191660a89290921b67ffffffffffffffff60a81b16919091179055565b604080825281018490525f8560608301825b8781101561325957823561323c8161273a565b6001600160a01b0316825260209283019290910190600101613229565b50838103602080860191909152858252019050845f5b85811015613306578135613282816130f6565b63ffffffff16835260208201356132988161273a565b6001600160a01b0316602084015260408201356132b48161273a565b6001600160a01b0316604084015260608201356132d081612cae565b1515606084015260808201356132e581612d31565b6001600160401b0316608084015260a092830192919091019060010161326f565b5090979650505050505050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc78555946a409defb00fb08ff23c8988ad687a02e1525a4fc9b7fd83443409e00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300349c08c4c7eff6f18e11e7787aea968e8ed1e26c7307dfcd722a9d753313069da164736f6c634300081c000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778

-----Decoded View---------------
Arg [0] : _binSponsor (uint8): 1
Arg [1] : _dataProvider (address): 0xDC515Cb479a64552c5A11a57109C314E40A1A778

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [1] : 000000000000000000000000dc515cb479a64552c5a11a57109c314e40a1a778


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.